@vex-chat/spire 3.0.1 → 4.1.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.
- package/README.md +9 -8
- package/dist/Database.d.ts +20 -11
- package/dist/Database.js +229 -118
- package/dist/Database.js.map +1 -1
- package/dist/Spire.d.ts +4 -2
- package/dist/Spire.js +155 -72
- package/dist/Spire.js.map +1 -1
- package/dist/db/schema.d.ts +0 -2
- package/dist/migrations/2026-04-06_initial-schema.js +4 -5
- package/dist/migrations/2026-04-06_initial-schema.js.map +1 -1
- package/dist/migrations/{2026-04-14_argon2id-password-hashing.d.ts → 2026-07-14_query-indexes.d.ts} +1 -1
- package/dist/migrations/2026-07-14_query-indexes.js +31 -0
- package/dist/migrations/2026-07-14_query-indexes.js.map +1 -0
- package/dist/server/avatar.js +33 -6
- package/dist/server/avatar.js.map +1 -1
- package/dist/server/cliPasskeyPage.js +109 -11
- package/dist/server/cliPasskeyPage.js.map +1 -1
- package/dist/server/errors.js +8 -0
- package/dist/server/errors.js.map +1 -1
- package/dist/server/file.js +35 -12
- package/dist/server/file.js.map +1 -1
- package/dist/server/index.d.ts +8 -0
- package/dist/server/index.js +319 -56
- package/dist/server/index.js.map +1 -1
- package/dist/server/passkey.js +367 -29
- package/dist/server/passkey.js.map +1 -1
- package/dist/server/passkeyDevices.js +1 -0
- package/dist/server/passkeyDevices.js.map +1 -1
- package/dist/server/password.d.ts +7 -0
- package/dist/server/password.js +58 -0
- package/dist/server/password.js.map +1 -0
- package/dist/server/permissions.d.ts +1 -0
- package/dist/server/permissions.js +11 -2
- package/dist/server/permissions.js.map +1 -1
- package/dist/server/rateLimit.d.ts +11 -0
- package/dist/server/rateLimit.js +43 -4
- package/dist/server/rateLimit.js.map +1 -1
- package/dist/server/serverIcon.d.ts +10 -0
- package/dist/server/serverIcon.js +158 -0
- package/dist/server/serverIcon.js.map +1 -0
- package/dist/server/user.d.ts +1 -1
- package/dist/server/user.js +40 -9
- package/dist/server/user.js.map +1 -1
- package/dist/types/express.d.ts +3 -4
- package/dist/types/express.js.map +1 -1
- package/dist/utils/authJwt.d.ts +8 -0
- package/dist/utils/authJwt.js +25 -0
- package/dist/utils/authJwt.js.map +1 -0
- package/dist/utils/loadEnv.js +4 -0
- package/dist/utils/loadEnv.js.map +1 -1
- package/dist/utils/preKeySignature.d.ts +1 -1
- package/dist/utils/preKeySignature.js +7 -11
- package/dist/utils/preKeySignature.js.map +1 -1
- package/package.json +7 -7
- package/src/Database.ts +294 -152
- package/src/Spire.ts +412 -285
- package/src/__tests__/Database.spec.ts +126 -3
- package/src/__tests__/browserPasskeyAuthentication.spec.ts +192 -0
- package/src/__tests__/browserPasskeyRegistration.spec.ts +182 -0
- package/src/__tests__/cliPasskeyPage.spec.ts +6 -0
- package/src/__tests__/connectAuth.spec.ts +146 -4
- package/src/__tests__/deviceTokenRevalidation.spec.ts +57 -3
- package/src/__tests__/passkeyDevices.spec.ts +94 -0
- package/src/__tests__/passkeys.spec.ts +7 -2
- package/src/__tests__/password.spec.ts +223 -0
- package/src/__tests__/permissions.spec.ts +50 -0
- package/src/__tests__/preKeySignature.spec.ts +20 -3
- package/src/__tests__/serverManagement.spec.ts +262 -0
- package/src/db/schema.ts +0 -2
- package/src/migrations/2026-04-06_initial-schema.ts +4 -5
- package/src/migrations/2026-07-14_query-indexes.ts +34 -0
- package/src/server/avatar.ts +32 -6
- package/src/server/cliPasskeyPage.ts +109 -11
- package/src/server/errors.ts +7 -0
- package/src/server/file.ts +34 -13
- package/src/server/index.ts +494 -139
- package/src/server/passkey.ts +531 -31
- package/src/server/passkeyDevices.ts +1 -0
- package/src/server/password.ts +96 -0
- package/src/server/permissions.ts +20 -2
- package/src/server/rateLimit.ts +47 -4
- package/src/server/serverIcon.ts +199 -0
- package/src/server/user.ts +44 -9
- package/src/types/express.ts +3 -4
- package/src/utils/authJwt.ts +34 -0
- package/src/utils/loadEnv.ts +4 -0
- package/src/utils/preKeySignature.ts +12 -15
- package/dist/migrations/2026-04-14_argon2id-password-hashing.js +0 -17
- package/dist/migrations/2026-04-14_argon2id-password-hashing.js.map +0 -1
- package/src/migrations/2026-04-14_argon2id-password-hashing.ts +0 -24
package/src/server/file.ts
CHANGED
|
@@ -14,7 +14,7 @@ import * as path from "node:path";
|
|
|
14
14
|
import express from "express";
|
|
15
15
|
|
|
16
16
|
import { XUtils } from "@vex-chat/crypto";
|
|
17
|
-
import { FilePayloadSchema } from "@vex-chat/types";
|
|
17
|
+
import { FilePayloadSchema, MAX_FILE_UPLOAD_BYTES } from "@vex-chat/types";
|
|
18
18
|
|
|
19
19
|
import multer from "multer";
|
|
20
20
|
import { z } from "zod/v4";
|
|
@@ -27,6 +27,14 @@ import { getParam } from "./utils.ts";
|
|
|
27
27
|
import { protect } from "./index.ts";
|
|
28
28
|
|
|
29
29
|
const safePathParam = z.string().regex(/^[a-zA-Z0-9._-]+$/);
|
|
30
|
+
const fileUpload = multer({
|
|
31
|
+
limits: {
|
|
32
|
+
fields: 4,
|
|
33
|
+
files: 1,
|
|
34
|
+
fileSize: MAX_FILE_UPLOAD_BYTES,
|
|
35
|
+
parts: 5,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
30
38
|
|
|
31
39
|
export const getFileRouter = (db: Database) => {
|
|
32
40
|
const router = express.Router();
|
|
@@ -77,7 +85,7 @@ export const getFileRouter = (db: Database) => {
|
|
|
77
85
|
}
|
|
78
86
|
});
|
|
79
87
|
|
|
80
|
-
router.post("/json", protect, async (req, res) => {
|
|
88
|
+
router.post("/json", uploadLimiter, protect, async (req, res) => {
|
|
81
89
|
const deviceDetails = req.device;
|
|
82
90
|
|
|
83
91
|
if (!deviceDetails) {
|
|
@@ -95,17 +103,28 @@ export const getFileRouter = (db: Database) => {
|
|
|
95
103
|
}
|
|
96
104
|
const payload = parsed.data;
|
|
97
105
|
|
|
98
|
-
if (payload.
|
|
106
|
+
if (!payload.file) {
|
|
99
107
|
res.sendStatus(400);
|
|
100
108
|
return;
|
|
101
109
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
110
|
+
if (payload.owner !== deviceDetails.deviceID) {
|
|
111
|
+
res.status(400).send({
|
|
112
|
+
error: "File owner must match this device.",
|
|
113
|
+
});
|
|
105
114
|
return;
|
|
106
115
|
}
|
|
107
116
|
|
|
108
|
-
|
|
117
|
+
let buf: Buffer;
|
|
118
|
+
try {
|
|
119
|
+
buf = Buffer.from(XUtils.decodeBase64(payload.file));
|
|
120
|
+
} catch {
|
|
121
|
+
res.status(400).send({ error: "File must be valid base64." });
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (buf.byteLength > MAX_FILE_UPLOAD_BYTES) {
|
|
125
|
+
res.sendStatus(413);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
109
128
|
|
|
110
129
|
const newFile: FileSQL = {
|
|
111
130
|
fileID: crypto.randomUUID(),
|
|
@@ -120,20 +139,20 @@ export const getFileRouter = (db: Database) => {
|
|
|
120
139
|
|
|
121
140
|
// Multipart file upload — form fields are strings from multer, not full FilePayload
|
|
122
141
|
const multipartFields = z.object({
|
|
123
|
-
nonce: z.string().
|
|
124
|
-
owner: z.string().min(1),
|
|
142
|
+
nonce: z.string().regex(/^[0-9a-fA-F]{48}$/),
|
|
143
|
+
owner: z.string().min(1).max(128),
|
|
125
144
|
});
|
|
126
145
|
|
|
127
146
|
router.post(
|
|
128
147
|
"/",
|
|
129
148
|
uploadLimiter,
|
|
130
149
|
protect,
|
|
131
|
-
|
|
150
|
+
fileUpload.single("file"),
|
|
132
151
|
async (req, res) => {
|
|
133
152
|
const deviceDetails = req.device;
|
|
134
153
|
|
|
135
154
|
if (!deviceDetails) {
|
|
136
|
-
res.sendStatus(
|
|
155
|
+
res.sendStatus(401);
|
|
137
156
|
return;
|
|
138
157
|
}
|
|
139
158
|
|
|
@@ -152,8 +171,10 @@ export const getFileRouter = (db: Database) => {
|
|
|
152
171
|
return;
|
|
153
172
|
}
|
|
154
173
|
|
|
155
|
-
if (payload.
|
|
156
|
-
res.
|
|
174
|
+
if (payload.owner !== deviceDetails.deviceID) {
|
|
175
|
+
res.status(400).send({
|
|
176
|
+
error: "File owner must match this device.",
|
|
177
|
+
});
|
|
157
178
|
return;
|
|
158
179
|
}
|
|
159
180
|
|