@vex-chat/spire 0.7.4 → 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.
- package/README.md +82 -26
- package/dist/ClientManager.d.ts +24 -25
- package/dist/ClientManager.js +232 -509
- package/dist/ClientManager.js.map +1 -0
- package/dist/Database.d.ts +49 -41
- package/dist/Database.js +698 -716
- package/dist/Database.js.map +1 -0
- package/dist/Spire.d.ts +23 -15
- package/dist/Spire.js +518 -218
- package/dist/Spire.js.map +1 -0
- package/dist/__tests__/Database.spec.js +113 -73
- package/dist/__tests__/Database.spec.js.map +1 -0
- package/dist/db/schema.d.ts +134 -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/run.js +26 -21
- package/dist/run.js.map +1 -0
- package/dist/server/avatar.d.ts +3 -4
- package/dist/server/avatar.js +85 -67
- package/dist/server/avatar.js.map +1 -0
- package/dist/server/errors.d.ts +59 -0
- package/dist/server/errors.js +94 -0
- package/dist/server/errors.js.map +1 -0
- package/dist/server/file.d.ts +3 -4
- package/dist/server/file.js +101 -61
- package/dist/server/file.js.map +1 -0
- package/dist/server/index.d.ts +9 -6
- package/dist/server/index.js +595 -70
- package/dist/server/index.js.map +1 -0
- package/dist/server/invite.d.ts +4 -5
- package/dist/server/invite.js +21 -103
- 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 +4 -7
- package/dist/server/user.js +66 -76
- 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/createLogger.js +13 -19
- package/dist/utils/createLogger.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 +15 -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 +91 -63
- package/src/ClientManager.ts +434 -0
- package/src/Database.ts +925 -0
- package/src/Spire.ts +878 -0
- package/src/__tests__/Database.spec.ts +167 -0
- package/src/ambient-modules.d.ts +1 -0
- package/src/db/schema.ts +165 -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/run.ts +37 -0
- package/src/server/avatar.ts +141 -0
- package/src/server/errors.ts +133 -0
- package/src/server/file.ts +172 -0
- package/src/server/index.ts +855 -0
- package/src/server/invite.ts +65 -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 +125 -0
- package/src/server/utils.ts +59 -0
- package/src/types/express.ts +23 -0
- package/src/utils/createLogger.ts +47 -0
- package/src/utils/createUint8UUID.ts +9 -0
- package/src/utils/jwtSecret.ts +16 -0
- package/src/utils/loadEnv.ts +15 -0
- package/src/utils/msgpack.ts +4 -0
- package/avatars/169d76cb-6e7c-4e24-8224-017673eed8ff +0 -0
- package/avatars/1cae8d0b-0c6b-4c73-b25c-2d2349a57122 +0 -0
- package/avatars/1d87bc2b-71fc-4818-8004-40d04093e5fc +0 -0
- package/avatars/1f2c3d62-8b4d-465a-9895-51a24caef00d +0 -0
- package/avatars/245ee7fc-1004-41ab-adab-a04c9ceb9d7a +0 -0
- package/avatars/2465c28c-bdaf-4fa2-b42a-d054f04dc39b +0 -0
- package/avatars/3900a674-a2dd-4996-a61d-8c29b3270f41 +0 -0
- package/avatars/3c3b9c77-ea50-45e7-bb25-65d6f3d2a219 +0 -0
- package/avatars/414a2ff4-ad2f-4a7d-aa27-3b09ad3522b2 +0 -0
- package/avatars/522fe504-f0ad-4ed4-9dc6-e5dc2338e531 +0 -0
- package/avatars/53e5eb29-e7d1-4d58-add9-d44f39f0cfb7 +0 -0
- package/avatars/54d3f757-1038-41c8-bfb9-efd37b6e8ebe +0 -0
- package/avatars/623e86d7-c49c-46f6-9b76-ca70c9dbc43b +0 -0
- package/avatars/66e2abae-60f5-4dd1-b9fb-297a4bedfeb0 +0 -0
- package/avatars/6f37980e-f6fa-4d6d-9206-24050b403f45 +0 -0
- package/avatars/80138ece-eb5c-4b20-817b-903d6b0f54ae +0 -0
- package/avatars/841c77d3-37c4-431b-be22-672888062874 +0 -0
- package/avatars/88051a61-2bda-4750-95a3-5fb0b4918149 +0 -0
- package/avatars/89540973-c421-4bf1-8b89-9f1eaa51c1b5 +0 -0
- package/avatars/8a802fad-8c99-4942-8f80-47cec600149c +0 -0
- package/avatars/90531d8a-907a-4a1a-ac45-c85e4acb0df9 +0 -0
- package/avatars/9b7d0da9-b8d6-4801-b128-9993f79f464a +0 -0
- package/avatars/9bc456f1-c4c4-48a1-b9e6-fd44dd744a72 +0 -0
- package/avatars/9cf878bf-7430-49ec-a47a-78f3c93793dd +0 -0
- package/avatars/9ee82847-6ad3-45e5-92b9-f474a6c54d96 +0 -0
- package/avatars/ab44c857-d81d-4c88-85db-32f9532e5376 +0 -0
- package/avatars/b396a8d2-dc14-48d2-aac4-3755dc637051 +0 -0
- package/avatars/b6ac11c5-a8b2-4e0a-995a-9c87f1e58787 +0 -0
- package/avatars/b79d6855-b738-434c-be32-809637e62b9b +0 -0
- package/avatars/bbcd0188-d6a5-48ae-90fb-be5ff30599ab +0 -0
- package/avatars/bc7a9e0e-4720-4a6e-a90d-c11fec94d380 +0 -0
- package/avatars/c1c4889f-8383-4041-8bdd-9fded4046f37 +0 -0
- package/avatars/c4c7203c-d93a-4749-ade2-17053acf1d2a +0 -0
- package/avatars/ca974a70-0a23-4668-8b80-c4304dc7f793 +0 -0
- package/avatars/cf119a0d-eb3f-4bed-905b-f14a876c3535 +0 -0
- package/avatars/d464b03d-30c2-49e3-a666-80aefa8a1b35 +0 -0
- package/avatars/da0eee89-82f0-4d45-ab48-7d2786b634c5 +0 -0
- package/avatars/de4c77a5-68e9-4bb2-b40d-d964bf377d61 +0 -0
- package/avatars/dea95395-7d0b-42aa-a9ed-40c7d4fb4c48 +0 -0
- package/avatars/edb30749-59ba-4aa2-9c52-0fb22048f4cf +0 -0
- package/avatars/f17c245a-af7d-445b-9365-49f7f54b1eeb +0 -0
- package/avatars/f1ee6a35-b262-4dbf-99f5-3d011e3b98ec +0 -0
- package/avatars/f802bdd0-345d-41f6-9184-0f30e1258fb3 +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/files/00ea7368-45a7-4f6a-a199-974da14be1a0 +0 -0
- package/files/039503d2-a170-4962-b921-c97994ba64ff +0 -0
- package/files/14b6fa02-4cbb-40df-be4f-a07187cb619e +0 -0
- package/files/15c04cb1-dc6a-4f19-aa6f-4a3b92d05bf7 +0 -0
- package/files/2a8d411c-8b92-4532-b84d-d64c638d6293 +0 -0
- package/files/37de2cd3-08a8-4044-9c37-e13386765f3d +0 -0
- package/files/42452029-284e-4c81-9f18-feb6ce309eed +0 -0
- package/files/43d09f2f-29c8-415f-8c4a-23f2a32eb79e +0 -0
- package/files/52992923-33ab-44a1-8118-605e9b4856a7 +0 -0
- package/files/53180681-36e2-49c0-8382-94dca0da09bf +0 -0
- package/files/5a56cd7b-1d04-4619-b60f-1e5515b9164a +0 -0
- package/files/5ced7676-20c4-4219-a4f2-70a25eb7eea8 +0 -0
- package/files/60e787ff-8ec5-444d-b963-0aaf5313b53e +0 -0
- package/files/67a17729-fcb7-4339-9499-1fc08fea72ca +0 -0
- package/files/68d09565-908d-4a67-8f09-e183f8708eb4 +0 -0
- package/files/70e587c5-56e8-47f0-bd36-691efcb0cc2e +0 -4
- package/files/7a227619-b715-4e2c-a79d-b2158d56a799 +0 -0
- package/files/7e3cc3ea-b706-4835-994d-65d33acaf369 +0 -0
- package/files/816f72a0-65dc-40c1-8862-1d22065cca3c +0 -0
- package/files/8bf84972-5086-4631-a752-093d7b1a098b +0 -0
- package/files/8c46e3bc-3f2e-441d-b8cc-17428fc3d219 +0 -0
- package/files/8eb83364-8826-4eee-895a-ba5cd3ab85e9 +0 -0
- package/files/8faefaea-14e3-49e4-ac74-9710622bfae9 +0 -0
- package/files/91af08c2-9dca-41f4-b6c6-b7bf33505df9 +0 -0
- package/files/9349dffa-35dd-49a0-a651-10b438038f95 +0 -0
- package/files/b0a12a41-6283-4f27-b2c8-66774991d96c +0 -0
- package/files/b28afb08-d18b-48a8-93c3-6a59c66d8fee +0 -0
- package/files/be60fe01-1578-4363-a908-41500092b77d +0 -0
- package/files/cf7b90e3-734a-4453-9ff3-9a331404b5ef +0 -0
- package/files/d1be30aa-8ff4-41c1-b360-f65922029047 +0 -0
- package/files/d2d31a57-a413-443c-9b97-fa9ca394ef0b +0 -0
- package/files/d357f223-b786-478d-a113-b53cb62acdab +0 -0
- package/files/d4a69ee0-05c4-4ff0-8753-624cdd0f24e9 +0 -0
- package/files/d57f411b-1874-4f00-a6b0-2f86de6b229d +0 -0
- package/files/d85aaa33-7f70-4a70-b3d2-cad667beb38c +0 -0
- package/files/db2fc236-dbe0-4d24-bfaf-884829fd090a +0 -0
- package/files/df2e20ec-6dcc-4402-94ee-7d1163f84197 +0 -0
- package/files/e0880ab5-6d49-4dc0-a5ec-0d3793a8a7b8 +0 -0
- package/files/e59ee9c6-5aea-48ea-b3d9-94a324dc2260 +0 -0
- package/files/e6d7aad6-4220-4ce7-85f8-89d0b1f0cc89 +0 -0
- package/files/f0bfc98c-3534-459d-931a-7c6e77bde153 +0 -0
- package/files/f8443740-050c-4714-9bf6-334783ae6ffd +0 -0
- package/files/fc405ae7-f9fb-455d-ac8c-0ca0d88d4115 +0 -0
- package/jest.config.js +0 -13
- package/spire.sqlite +0 -0
package/dist/ClientManager.js
CHANGED
|
@@ -1,510 +1,115 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
};
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.ClientManager = exports.POWER_LEVELS = void 0;
|
|
16
|
-
const sleep_1 = require("@extrahash/sleep");
|
|
17
|
-
const crypto_1 = require("@vex-chat/crypto");
|
|
18
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
19
|
-
const events_1 = require("events");
|
|
20
|
-
const msgpack_lite_1 = __importDefault(require("msgpack-lite"));
|
|
21
|
-
const tweetnacl_1 = __importDefault(require("tweetnacl"));
|
|
22
|
-
const uuid_1 = require("uuid");
|
|
23
|
-
const utils_1 = require("./server/utils");
|
|
24
|
-
const Spire_1 = require("./Spire");
|
|
25
|
-
const createLogger_1 = require("./utils/createLogger");
|
|
26
|
-
const createUint8UUID_1 = require("./utils/createUint8UUID");
|
|
27
|
-
exports.POWER_LEVELS = {
|
|
28
|
-
INVITE: 25,
|
|
1
|
+
import { EventEmitter } from "events";
|
|
2
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
3
|
+
import { xConcat, XUtils } from "@vex-chat/crypto";
|
|
4
|
+
import { xSignOpen } from "@vex-chat/crypto";
|
|
5
|
+
import { MailWSSchema, SocketAuthErrors } from "@vex-chat/types";
|
|
6
|
+
import pc from "picocolors";
|
|
7
|
+
import { parse as uuidParse, validate as uuidValidate } from "uuid";
|
|
8
|
+
import { TOKEN_EXPIRY } from "./Spire.js";
|
|
9
|
+
import { createLogger } from "./utils/createLogger.js";
|
|
10
|
+
import { createUint8UUID } from "./utils/createUint8UUID.js";
|
|
11
|
+
import { msgpack } from "./utils/msgpack.js";
|
|
12
|
+
export const POWER_LEVELS = {
|
|
29
13
|
CREATE: 50,
|
|
30
14
|
DELETE: 50,
|
|
15
|
+
EMOJI: 25,
|
|
16
|
+
INVITE: 25,
|
|
31
17
|
};
|
|
32
18
|
function emptyHeader() {
|
|
33
19
|
return new Uint8Array(32);
|
|
34
20
|
}
|
|
35
21
|
const MAX_MSG_SIZE = 2048;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
constructor(ws, db, notify,
|
|
22
|
+
export class ClientManager extends EventEmitter {
|
|
23
|
+
alive = true;
|
|
24
|
+
authed = false;
|
|
25
|
+
challengeID = createUint8UUID();
|
|
26
|
+
conn;
|
|
27
|
+
db;
|
|
28
|
+
device;
|
|
29
|
+
failed = false;
|
|
30
|
+
log;
|
|
31
|
+
notify;
|
|
32
|
+
user;
|
|
33
|
+
userDetails;
|
|
34
|
+
constructor(ws, db, notify, userDetails, options) {
|
|
49
35
|
super();
|
|
50
|
-
this.authed = false;
|
|
51
|
-
this.alive = true;
|
|
52
|
-
this.challengeID = createUint8UUID_1.createUint8UUID();
|
|
53
|
-
this.failed = false;
|
|
54
36
|
this.conn = ws;
|
|
55
37
|
this.db = db;
|
|
56
38
|
this.user = null;
|
|
57
|
-
this.
|
|
39
|
+
this.userDetails = userDetails;
|
|
58
40
|
this.device = null;
|
|
59
41
|
this.notify = notify;
|
|
60
|
-
this.log =
|
|
42
|
+
this.log = createLogger("client-manager", options?.logLevel || "error");
|
|
61
43
|
this.initListeners();
|
|
62
44
|
this.challenge();
|
|
63
45
|
}
|
|
64
|
-
|
|
65
|
-
if (!this.
|
|
66
|
-
|
|
46
|
+
getDevice() {
|
|
47
|
+
if (!this.device) {
|
|
48
|
+
throw new Error("No device set on this client.");
|
|
67
49
|
}
|
|
68
|
-
return this.
|
|
50
|
+
return this.device;
|
|
69
51
|
}
|
|
70
52
|
getUser() {
|
|
71
|
-
if (!this.authed) {
|
|
53
|
+
if (!this.authed || !this.user) {
|
|
72
54
|
throw new Error("You must be authed before getting user info.");
|
|
73
55
|
}
|
|
74
56
|
return this.user;
|
|
75
57
|
}
|
|
76
58
|
send(msg, header) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
});
|
|
59
|
+
if (header) {
|
|
60
|
+
this.log.debug(pc.bold(pc.red("OUTH")), header.toString());
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
this.log.debug(pc.bold(pc.red("OUTH")), emptyHeader.toString());
|
|
64
|
+
}
|
|
65
|
+
const packedMessage = packMessage(msg, header);
|
|
66
|
+
this.log.info(pc.bold("⟶ ") +
|
|
67
|
+
responseColor(msg.type.toUpperCase()) +
|
|
68
|
+
" " +
|
|
69
|
+
this.toString() +
|
|
70
|
+
" " +
|
|
71
|
+
pc.yellow(Buffer.byteLength(packedMessage)));
|
|
72
|
+
this.log.debug(pc.bold(pc.red("OUT")), msg);
|
|
73
|
+
try {
|
|
74
|
+
this.conn.send(packedMessage);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
this.log.warn(String(err));
|
|
78
|
+
this.fail();
|
|
79
|
+
}
|
|
100
80
|
}
|
|
101
|
-
|
|
102
|
-
|
|
81
|
+
toString() {
|
|
82
|
+
if (!this.user || !this.device) {
|
|
83
|
+
return "Unauthorized#0000";
|
|
84
|
+
}
|
|
85
|
+
return this.user.username + "<" + this.getDevice().deviceID + ">";
|
|
103
86
|
}
|
|
104
87
|
authorize(transmissionID) {
|
|
105
88
|
this.authed = true;
|
|
106
89
|
this.sendAuthedMessage(transmissionID);
|
|
90
|
+
void this.db.markDeviceLogin(this.getDevice());
|
|
107
91
|
this.emit("authed");
|
|
108
92
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
});
|
|
93
|
+
challenge() {
|
|
94
|
+
this.challengeID = new Uint8Array(uuidParse(crypto.randomUUID()));
|
|
95
|
+
const challenge = {
|
|
96
|
+
challenge: this.challengeID,
|
|
97
|
+
transmissionID: crypto.randomUUID(),
|
|
98
|
+
type: "challenge",
|
|
99
|
+
};
|
|
100
|
+
this.send(challenge);
|
|
118
101
|
}
|
|
119
102
|
fail() {
|
|
120
103
|
if (this.failed) {
|
|
121
104
|
return;
|
|
122
105
|
}
|
|
123
106
|
this.log.warn("Connection closed.");
|
|
124
|
-
|
|
125
|
-
this.conn.close();
|
|
126
|
-
}
|
|
107
|
+
this.conn.close();
|
|
127
108
|
this.failed = true;
|
|
128
109
|
this.emit("fail");
|
|
129
110
|
}
|
|
130
|
-
|
|
131
|
-
this.
|
|
132
|
-
}
|
|
133
|
-
pingLoop() {
|
|
134
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
135
|
-
while (true) {
|
|
136
|
-
this.ping();
|
|
137
|
-
yield sleep_1.sleep(5000);
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
ping() {
|
|
142
|
-
if (!this.alive) {
|
|
143
|
-
this.fail();
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
this.setAlive(false);
|
|
147
|
-
const p = { transmissionID: uuid_1.v4(), type: "ping" };
|
|
148
|
-
this.send(p);
|
|
149
|
-
}
|
|
150
|
-
pong(transmissionID) {
|
|
151
|
-
// ping is allowed before auth
|
|
152
|
-
if (this.user) {
|
|
153
|
-
this.db.markUserSeen(this.user);
|
|
154
|
-
}
|
|
155
|
-
const p = { transmissionID, type: "pong" };
|
|
156
|
-
this.send(p);
|
|
157
|
-
}
|
|
158
|
-
verifyResponse(msg) {
|
|
159
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
160
|
-
const user = yield this.db.retrieveUser(this.jwtDetails.userID);
|
|
161
|
-
if (user) {
|
|
162
|
-
const devices = yield this.db.retrieveUserDeviceList([user.userID]);
|
|
163
|
-
let message = null;
|
|
164
|
-
for (const device of devices) {
|
|
165
|
-
const verified = tweetnacl_1.default.sign.open(msg.signed, crypto_1.XUtils.decodeHex(device.signKey));
|
|
166
|
-
if (verified) {
|
|
167
|
-
message = verified;
|
|
168
|
-
this.device = device;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
if (!message) {
|
|
172
|
-
console.warn("Bad response from client.");
|
|
173
|
-
this.fail();
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
if (message) {
|
|
177
|
-
if (crypto_1.XUtils.bytesEqual(this.challengeID, message)) {
|
|
178
|
-
this.user = user;
|
|
179
|
-
this.authorize(msg.transmissionID);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
else {
|
|
183
|
-
this.log.info("Signature verification failed!");
|
|
184
|
-
this.fail();
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
this.log.info("User is not registered.");
|
|
189
|
-
this.fail();
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
challenge() {
|
|
194
|
-
this.challengeID = new Uint8Array(uuid_1.parse(uuid_1.v4()));
|
|
195
|
-
const challenge = {
|
|
196
|
-
transmissionID: uuid_1.v4(),
|
|
197
|
-
type: "challenge",
|
|
198
|
-
challenge: this.challengeID,
|
|
199
|
-
};
|
|
200
|
-
this.send(challenge);
|
|
201
|
-
}
|
|
202
|
-
sendErr(transmissionID, message, data) {
|
|
203
|
-
const error = {
|
|
204
|
-
transmissionID,
|
|
205
|
-
type: "error",
|
|
206
|
-
error: message,
|
|
207
|
-
data,
|
|
208
|
-
};
|
|
209
|
-
this.send(error);
|
|
210
|
-
}
|
|
211
|
-
sendAuthedMessage(transmissionID) {
|
|
212
|
-
this.send({ type: "authorized", transmissionID });
|
|
213
|
-
}
|
|
214
|
-
sendSuccess(transmissionID, data, header) {
|
|
215
|
-
const msg = {
|
|
216
|
-
transmissionID,
|
|
217
|
-
type: "success",
|
|
218
|
-
data,
|
|
219
|
-
};
|
|
220
|
-
this.send(msg, header);
|
|
221
|
-
}
|
|
222
|
-
parseResourceMsg(msg, header) {
|
|
223
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
224
|
-
switch (msg.resourceType) {
|
|
225
|
-
case "permissions":
|
|
226
|
-
if (msg.action === "RETRIEVE") {
|
|
227
|
-
try {
|
|
228
|
-
const permissions = yield this.db.retrievePermissions(this.getUser().userID, "all");
|
|
229
|
-
this.sendSuccess(msg.transmissionID, permissions);
|
|
230
|
-
break;
|
|
231
|
-
}
|
|
232
|
-
catch (err) {
|
|
233
|
-
this.sendErr(msg.transmissionID, err.toString());
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
if (msg.action === "DELETE") {
|
|
237
|
-
try {
|
|
238
|
-
// msg.data is permID
|
|
239
|
-
const permToDelete = yield this.db.retrievePermission(msg.data);
|
|
240
|
-
if (!permToDelete) {
|
|
241
|
-
this.sendErr(msg.transmissionID, "That doesn't exist.");
|
|
242
|
-
break;
|
|
243
|
-
}
|
|
244
|
-
const permissions = yield this.db.retrievePermissions(this.getUser().userID, permToDelete.resourceType);
|
|
245
|
-
let found = false;
|
|
246
|
-
for (const perm of permissions) {
|
|
247
|
-
// msg.data is resourceID
|
|
248
|
-
if (perm.resourceID === permToDelete.resourceID &&
|
|
249
|
-
(perm.userID === this.getUser().userID ||
|
|
250
|
-
(perm.powerLevel > exports.POWER_LEVELS.DELETE &&
|
|
251
|
-
perm.powerLevel >
|
|
252
|
-
permToDelete.powerLevel))) {
|
|
253
|
-
this.db.deletePermission(perm.permissionID);
|
|
254
|
-
this.sendSuccess(msg.transmissionID, null);
|
|
255
|
-
found = true;
|
|
256
|
-
break;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
if (!found) {
|
|
260
|
-
this.sendErr(msg.transmissionID, "You don't have permission to do that.");
|
|
261
|
-
break;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
catch (err) {
|
|
265
|
-
this.sendErr(msg.transmissionID, err.toString());
|
|
266
|
-
break;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
if (msg.action === "CREATE") {
|
|
270
|
-
try {
|
|
271
|
-
const { resourceType, userID, resourceID } = msg.data;
|
|
272
|
-
const userHeldPerms = yield this.db.retrievePermissions(this.getUser().userID, "all");
|
|
273
|
-
let found = false;
|
|
274
|
-
for (const perm of userHeldPerms) {
|
|
275
|
-
if (perm.resourceID === resourceID) {
|
|
276
|
-
if (perm.powerLevel > exports.POWER_LEVELS.CREATE) {
|
|
277
|
-
// he's got the perm and the power level, we're good to go
|
|
278
|
-
const newPerm = yield this.db.createPermission(userID, resourceType, resourceID, 0);
|
|
279
|
-
this.sendSuccess(msg.transmissionID, newPerm);
|
|
280
|
-
// notify the user of their new permission
|
|
281
|
-
this.notify(userID, "permission", msg.transmissionID, newPerm);
|
|
282
|
-
found = true;
|
|
283
|
-
break;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
if (!found) {
|
|
288
|
-
this.sendErr(msg.transmissionID, "You don't have permission for that.");
|
|
289
|
-
}
|
|
290
|
-
break;
|
|
291
|
-
}
|
|
292
|
-
catch (err) {
|
|
293
|
-
this.sendErr(msg.transmissionID, err.toString());
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
break;
|
|
297
|
-
case "otk":
|
|
298
|
-
if (msg.action === "RETRIEVE") {
|
|
299
|
-
try {
|
|
300
|
-
const keyCount = yield this.db.getOTKCount(this.getDevice().deviceID);
|
|
301
|
-
this.sendSuccess(msg.transmissionID, keyCount);
|
|
302
|
-
}
|
|
303
|
-
catch (err) {
|
|
304
|
-
this.log.error(err);
|
|
305
|
-
this.sendErr(msg.transmissionID, err.toString());
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
if (msg.action === "CREATE") {
|
|
309
|
-
try {
|
|
310
|
-
yield this.db.saveOTK(this.getDevice().deviceID, msg.data);
|
|
311
|
-
this.sendSuccess(msg.transmissionID, msg);
|
|
312
|
-
}
|
|
313
|
-
catch (err) {
|
|
314
|
-
this.log.error(err);
|
|
315
|
-
this.sendErr(msg.transmissionID, err.toString());
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
break;
|
|
319
|
-
case "user":
|
|
320
|
-
if (msg.action === "RETRIEVE") {
|
|
321
|
-
try {
|
|
322
|
-
const user = yield this.db.retrieveUser(msg.data);
|
|
323
|
-
if (user) {
|
|
324
|
-
this.sendSuccess(msg.transmissionID, utils_1.censorUser(user));
|
|
325
|
-
}
|
|
326
|
-
else {
|
|
327
|
-
this.log.error("User doesn't exist.");
|
|
328
|
-
this.sendErr(msg.transmissionID, "That user doesn't exist.");
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
catch (err) {
|
|
332
|
-
this.log.error(err);
|
|
333
|
-
this.sendErr(msg.transmissionID, err.toString());
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
break;
|
|
337
|
-
// this is a single channel userlist
|
|
338
|
-
case "userlist":
|
|
339
|
-
if (msg.action === "RETRIEVE") {
|
|
340
|
-
const channelID = msg.data;
|
|
341
|
-
try {
|
|
342
|
-
const channel = yield this.db.retrieveChannel(channelID);
|
|
343
|
-
if (!channel) {
|
|
344
|
-
this.sendErr(msg.transmissionID, "That channel doesn't exist.");
|
|
345
|
-
break;
|
|
346
|
-
}
|
|
347
|
-
const permissions = yield this.db.retrievePermissions(this.getUser().userID, "server");
|
|
348
|
-
for (const permission of permissions) {
|
|
349
|
-
if (permission.resourceID === channel.serverID) {
|
|
350
|
-
// we've got the permission, it's ok to give them the userlist
|
|
351
|
-
const groupMembers = yield this.db.retrieveGroupMembers(channelID);
|
|
352
|
-
this.sendSuccess(msg.transmissionID, groupMembers.map((user) => utils_1.censorUser(user)));
|
|
353
|
-
break;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
catch (err) {
|
|
358
|
-
this.sendErr(msg.transmissionID, err.toString());
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
break;
|
|
362
|
-
case "keyBundle":
|
|
363
|
-
if (msg.action === "RETRIEVE") {
|
|
364
|
-
try {
|
|
365
|
-
const keyBundle = yield this.db.getKeyBundle(msg.data);
|
|
366
|
-
if (keyBundle) {
|
|
367
|
-
this.sendSuccess(msg.transmissionID, keyBundle);
|
|
368
|
-
}
|
|
369
|
-
else {
|
|
370
|
-
this.sendErr(msg.transmissionID, "Couldn't retrieve key bundle.");
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
catch (err) {
|
|
374
|
-
this.log.error(err);
|
|
375
|
-
this.sendErr(msg.transmissionID, err.toString());
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
break;
|
|
379
|
-
case "mail":
|
|
380
|
-
if (msg.action === "RETRIEVE") {
|
|
381
|
-
try {
|
|
382
|
-
const inbox = yield this.db.retrieveMail(this.getDevice().deviceID);
|
|
383
|
-
for (const mail of inbox) {
|
|
384
|
-
const [mailHeader, mailBody] = mail;
|
|
385
|
-
this.sendSuccess(msg.transmissionID, mailBody, mailHeader);
|
|
386
|
-
}
|
|
387
|
-
this.sendSuccess(msg.transmissionID, null);
|
|
388
|
-
}
|
|
389
|
-
catch (err) {
|
|
390
|
-
this.log.error(err);
|
|
391
|
-
this.sendErr(msg.transmissionID, err.toString());
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
if (msg.action === "CREATE") {
|
|
395
|
-
const mail = msg.data;
|
|
396
|
-
try {
|
|
397
|
-
yield this.db.saveMail(mail, header, this.getDevice().deviceID, this.getUser().userID);
|
|
398
|
-
this.log.info("Received mail for " + msg.data.recipient);
|
|
399
|
-
const deviceDetails = yield this.db.retrieveDevice(msg.data.recipient);
|
|
400
|
-
if (!deviceDetails) {
|
|
401
|
-
this.sendErr(msg.transmissionID, "No associated user record found for device.");
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
this.sendSuccess(msg.transmissionID, null);
|
|
405
|
-
this.notify(deviceDetails.owner, "mail", msg.transmissionID, null, msg.data.recipient);
|
|
406
|
-
}
|
|
407
|
-
catch (err) {
|
|
408
|
-
this.log.error(err);
|
|
409
|
-
this.sendErr(msg.transmissionID, err.toString());
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
break;
|
|
413
|
-
case "servers":
|
|
414
|
-
if (msg.action === "RETRIEVE") {
|
|
415
|
-
const servers = yield this.db.retrieveServers(this.getUser().userID);
|
|
416
|
-
this.sendSuccess(msg.transmissionID, servers);
|
|
417
|
-
}
|
|
418
|
-
if (msg.action === "CREATE") {
|
|
419
|
-
const server = yield this.db.createServer(msg.data, this.getUser().userID);
|
|
420
|
-
this.sendSuccess(msg.transmissionID, server);
|
|
421
|
-
}
|
|
422
|
-
if (msg.action === "DELETE") {
|
|
423
|
-
const permissions = yield this.db.retrievePermissions(this.getUser().userID, "server");
|
|
424
|
-
let found = false;
|
|
425
|
-
for (const permission of permissions) {
|
|
426
|
-
if (permission.resourceID === msg.data &&
|
|
427
|
-
permission.powerLevel > 50) {
|
|
428
|
-
// msg.data is the serverID
|
|
429
|
-
yield this.db.deleteServer(msg.data);
|
|
430
|
-
this.sendSuccess(msg.transmissionID, null);
|
|
431
|
-
found = true;
|
|
432
|
-
break;
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
if (!found) {
|
|
436
|
-
this.sendErr(msg.transmissionID, "You don't have permission to do that.");
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
break;
|
|
440
|
-
case "channels":
|
|
441
|
-
if (msg.action === "RETRIEVE") {
|
|
442
|
-
const permissions = yield this.db.retrievePermissions(this.getUser().userID, "server");
|
|
443
|
-
for (const permission of permissions) {
|
|
444
|
-
if (msg.data === permission.resourceID) {
|
|
445
|
-
const channels = yield this.db.retrieveChannels(permission.resourceID);
|
|
446
|
-
this.sendSuccess(msg.transmissionID, channels);
|
|
447
|
-
break;
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
this.sendSuccess(msg.transmissionID, []);
|
|
451
|
-
}
|
|
452
|
-
if (msg.action === "CREATE") {
|
|
453
|
-
// resourceID is serverID
|
|
454
|
-
const { serverID, name } = msg.data;
|
|
455
|
-
const permissions = yield this.db.retrievePermissions(this.getUser().userID, "server");
|
|
456
|
-
let found = false;
|
|
457
|
-
for (const permission of permissions) {
|
|
458
|
-
if (permission.resourceID === serverID &&
|
|
459
|
-
permission.powerLevel > exports.POWER_LEVELS.CREATE) {
|
|
460
|
-
const channel = yield this.db.createChannel(name, serverID);
|
|
461
|
-
this.sendSuccess(msg.transmissionID, channel);
|
|
462
|
-
found = true;
|
|
463
|
-
break;
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
if (!found) {
|
|
467
|
-
this.sendErr(msg.transmissionID, "You don't have permission to do that.");
|
|
468
|
-
}
|
|
469
|
-
else {
|
|
470
|
-
this.notifyServerChange(serverID, msg.transmissionID);
|
|
471
|
-
}
|
|
472
|
-
break;
|
|
473
|
-
}
|
|
474
|
-
if (msg.action === "DELETE") {
|
|
475
|
-
const channel = yield this.db.retrieveChannel(msg.data || "");
|
|
476
|
-
if (!channel) {
|
|
477
|
-
this.sendErr(msg.transmissionID, "You don't have permission to do that.");
|
|
478
|
-
break;
|
|
479
|
-
}
|
|
480
|
-
const permissions = yield this.db.retrievePermissions(this.getUser().userID, "server");
|
|
481
|
-
let found = false;
|
|
482
|
-
for (const permission of permissions) {
|
|
483
|
-
if (permission.resourceID === channel.serverID &&
|
|
484
|
-
permission.powerLevel > 50) {
|
|
485
|
-
found = true;
|
|
486
|
-
// msg.data is the channelID
|
|
487
|
-
yield this.db.deleteChannel(msg.data);
|
|
488
|
-
this.sendSuccess(msg.transmissionID, null);
|
|
489
|
-
this.notifyServerChange(channel.serverID, msg.transmissionID);
|
|
490
|
-
found = true;
|
|
491
|
-
break;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
if (!found) {
|
|
495
|
-
this.sendErr(msg.transmissionID, "You don't have permission to do that.");
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
break;
|
|
499
|
-
default:
|
|
500
|
-
this.log.info("Unsupported resource type " + msg.resourceType);
|
|
501
|
-
}
|
|
502
|
-
});
|
|
503
|
-
}
|
|
504
|
-
handleReceipt(msg) {
|
|
505
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
506
|
-
yield this.db.deleteMail(msg.nonce, this.getDevice().deviceID);
|
|
507
|
-
});
|
|
111
|
+
async handleReceipt(msg) {
|
|
112
|
+
await this.db.deleteMail(msg.nonce, this.getDevice().deviceID);
|
|
508
113
|
}
|
|
509
114
|
initListeners() {
|
|
510
115
|
this.conn.on("open", () => {
|
|
@@ -512,8 +117,8 @@ class ClientManager extends events_1.EventEmitter {
|
|
|
512
117
|
if (!this.authed) {
|
|
513
118
|
this.conn.close();
|
|
514
119
|
}
|
|
515
|
-
},
|
|
516
|
-
this.pingLoop();
|
|
120
|
+
}, TOKEN_EXPIRY);
|
|
121
|
+
void this.pingLoop();
|
|
517
122
|
});
|
|
518
123
|
this.conn.on("close", () => {
|
|
519
124
|
this.fail();
|
|
@@ -523,50 +128,49 @@ class ClientManager extends events_1.EventEmitter {
|
|
|
523
128
|
const size = Buffer.byteLength(message);
|
|
524
129
|
if (size > MAX_MSG_SIZE) {
|
|
525
130
|
this.sendErr(msg.transmissionID, "Message is too big. Received size " +
|
|
526
|
-
size +
|
|
131
|
+
String(size) +
|
|
527
132
|
" while max size is " +
|
|
528
|
-
MAX_MSG_SIZE);
|
|
133
|
+
String(MAX_MSG_SIZE));
|
|
529
134
|
return;
|
|
530
135
|
}
|
|
531
|
-
this.log.info(
|
|
532
|
-
(msg.type
|
|
533
|
-
? crudColor(msg.action.toUpperCase()) +
|
|
534
|
-
" " +
|
|
535
|
-
chalk_1.default.bold(msg.resourceType.toUpperCase())
|
|
536
|
-
: chalk_1.default.bold(msg.type.toUpperCase())) +
|
|
136
|
+
this.log.info(pc.bold("⟵ ") +
|
|
137
|
+
pc.bold(msg.type.toUpperCase()) +
|
|
537
138
|
" " +
|
|
538
139
|
this.toString() +
|
|
539
140
|
" " +
|
|
540
|
-
|
|
541
|
-
this.log.debug(
|
|
542
|
-
this.log.debug(
|
|
141
|
+
pc.yellow(String(size)));
|
|
142
|
+
this.log.debug(pc.bold(pc.red("INH")), header.toString());
|
|
143
|
+
this.log.debug(pc.bold(pc.red("IN")), msg);
|
|
543
144
|
if (!msg.type) {
|
|
544
145
|
this.sendErr(msg.transmissionID, "Message type is required.");
|
|
545
146
|
return;
|
|
546
147
|
}
|
|
547
|
-
if (!
|
|
548
|
-
this.sendErr(
|
|
148
|
+
if (!uuidValidate(msg.transmissionID)) {
|
|
149
|
+
this.sendErr(crypto.randomUUID(), "transmissionID is required and must be a valid uuid.");
|
|
549
150
|
return;
|
|
550
151
|
}
|
|
551
152
|
switch (msg.type) {
|
|
153
|
+
case "ping":
|
|
154
|
+
this.pong(msg.transmissionID);
|
|
155
|
+
break;
|
|
156
|
+
case "pong":
|
|
157
|
+
this.setAlive(true);
|
|
158
|
+
break;
|
|
552
159
|
case "receipt":
|
|
553
|
-
|
|
160
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- narrowed by msg.type
|
|
161
|
+
void this.handleReceipt(msg);
|
|
554
162
|
break;
|
|
555
163
|
case "resource":
|
|
556
164
|
if (!this.authed) {
|
|
557
165
|
this.sendErr(msg.transmissionID, "You are not authenticated.");
|
|
558
166
|
break;
|
|
559
167
|
}
|
|
560
|
-
|
|
168
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- narrowed by msg.type
|
|
169
|
+
void this.parseResourceMsg(msg, header);
|
|
561
170
|
break;
|
|
562
171
|
case "response":
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
case "ping":
|
|
566
|
-
this.pong(msg.transmissionID);
|
|
567
|
-
break;
|
|
568
|
-
case "pong":
|
|
569
|
-
this.setAlive(true);
|
|
172
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- narrowed by msg.type
|
|
173
|
+
void this.verifyResponse(msg);
|
|
570
174
|
break;
|
|
571
175
|
default:
|
|
572
176
|
this.log.info("unsupported message %s", msg.type);
|
|
@@ -574,29 +178,148 @@ class ClientManager extends events_1.EventEmitter {
|
|
|
574
178
|
}
|
|
575
179
|
});
|
|
576
180
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
181
|
+
async parseResourceMsg(msg, header) {
|
|
182
|
+
switch (msg.resourceType) {
|
|
183
|
+
case "mail":
|
|
184
|
+
if (msg.action === "CREATE") {
|
|
185
|
+
const mailResult = MailWSSchema.safeParse(msg.data);
|
|
186
|
+
if (!mailResult.success) {
|
|
187
|
+
this.sendErr(msg.transmissionID, "Invalid mail payload: " +
|
|
188
|
+
JSON.stringify(mailResult.error.issues));
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const mail = mailResult.data;
|
|
192
|
+
try {
|
|
193
|
+
await this.db.saveMail(mail, header, this.getDevice().deviceID, this.getUser().userID);
|
|
194
|
+
this.log.info("Received mail for " + mail.recipient);
|
|
195
|
+
const deviceDetails = await this.db.retrieveDevice(mail.recipient);
|
|
196
|
+
if (!deviceDetails) {
|
|
197
|
+
this.sendErr(msg.transmissionID, "No associated user record found for device.");
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
this.sendSuccess(msg.transmissionID, null);
|
|
201
|
+
this.notify(deviceDetails.owner, "mail", msg.transmissionID, null, mail.recipient);
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
this.log.error(String(err));
|
|
205
|
+
this.sendErr(msg.transmissionID, String(err));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
break;
|
|
209
|
+
default:
|
|
210
|
+
this.log.info("Unsupported resource type " + msg.resourceType);
|
|
211
|
+
}
|
|
591
212
|
}
|
|
592
|
-
|
|
213
|
+
ping() {
|
|
214
|
+
if (!this.alive) {
|
|
215
|
+
this.fail();
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
this.setAlive(false);
|
|
219
|
+
const p = { transmissionID: crypto.randomUUID(), type: "ping" };
|
|
220
|
+
this.send(p);
|
|
221
|
+
}
|
|
222
|
+
async pingLoop() {
|
|
223
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- intentional infinite loop
|
|
224
|
+
while (true) {
|
|
225
|
+
this.ping();
|
|
226
|
+
await sleep(5000);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
pong(transmissionID) {
|
|
230
|
+
// ping is allowed before auth
|
|
231
|
+
if (this.user) {
|
|
232
|
+
void this.db.markUserSeen(this.user);
|
|
233
|
+
}
|
|
234
|
+
const p = { transmissionID, type: "pong" };
|
|
235
|
+
this.send(p);
|
|
236
|
+
}
|
|
237
|
+
sendAuthedMessage(transmissionID) {
|
|
238
|
+
this.send({ transmissionID, type: "authorized" });
|
|
239
|
+
}
|
|
240
|
+
sendAuthError(error) {
|
|
241
|
+
const msg = {
|
|
242
|
+
error,
|
|
243
|
+
transmissionID: crypto.randomUUID(),
|
|
244
|
+
type: "authErr",
|
|
245
|
+
};
|
|
246
|
+
this.send(msg);
|
|
247
|
+
}
|
|
248
|
+
sendErr(transmissionID, message, data) {
|
|
249
|
+
const error = {
|
|
250
|
+
data,
|
|
251
|
+
error: message,
|
|
252
|
+
transmissionID,
|
|
253
|
+
type: "error",
|
|
254
|
+
};
|
|
255
|
+
this.send(error);
|
|
256
|
+
}
|
|
257
|
+
sendSuccess(transmissionID, data, header, timestamp) {
|
|
258
|
+
const msg = {
|
|
259
|
+
data,
|
|
260
|
+
timestamp,
|
|
261
|
+
transmissionID,
|
|
262
|
+
type: "success",
|
|
263
|
+
};
|
|
264
|
+
this.send(msg, header);
|
|
265
|
+
}
|
|
266
|
+
setAlive(status) {
|
|
267
|
+
this.alive = status;
|
|
268
|
+
}
|
|
269
|
+
async verifyResponse(msg) {
|
|
270
|
+
const user = await this.db.retrieveUser(this.userDetails.userID);
|
|
271
|
+
if (user) {
|
|
272
|
+
const devices = await this.db.retrieveUserDeviceList([user.userID]);
|
|
273
|
+
let message = null;
|
|
274
|
+
for (const device of devices) {
|
|
275
|
+
const verified = xSignOpen(msg.signed, XUtils.decodeHex(device.signKey));
|
|
276
|
+
if (verified) {
|
|
277
|
+
message = verified;
|
|
278
|
+
this.device = device;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (!message) {
|
|
282
|
+
this.log.warn("Signature verification failed!");
|
|
283
|
+
this.sendAuthError(SocketAuthErrors.BadSignature);
|
|
284
|
+
this.fail();
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (XUtils.bytesEqual(this.challengeID, message)) {
|
|
288
|
+
this.user = user;
|
|
289
|
+
this.authorize(msg.transmissionID);
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
this.log.warn("Token is bad!");
|
|
293
|
+
this.sendAuthError(SocketAuthErrors.InvalidToken);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
this.log.info("User is not registered.");
|
|
298
|
+
this.sendAuthError(SocketAuthErrors.UserNotRegistered);
|
|
299
|
+
this.fail();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function packMessage(msg, header) {
|
|
304
|
+
const msgb = Uint8Array.from(msgpack.encode(msg));
|
|
305
|
+
const msgh = header || emptyHeader();
|
|
306
|
+
return xConcat(msgh, msgb);
|
|
307
|
+
}
|
|
308
|
+
function unpackMessage(msg) {
|
|
309
|
+
const msgp = Uint8Array.from(msg);
|
|
310
|
+
const msgh = msgp.slice(0, 32);
|
|
311
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- msgpack.decode returns any
|
|
312
|
+
const msgb = msgpack.decode(msgp.slice(32));
|
|
313
|
+
return [msgh, msgb];
|
|
314
|
+
}
|
|
593
315
|
const responseColor = (status) => {
|
|
594
316
|
switch (status) {
|
|
595
|
-
case "SUCCESS":
|
|
596
|
-
return chalk_1.default.green.bold(status);
|
|
597
317
|
case "ERROR":
|
|
598
|
-
return
|
|
318
|
+
return pc.bold(pc.red(status));
|
|
319
|
+
case "SUCCESS":
|
|
320
|
+
return pc.bold(pc.green(status));
|
|
599
321
|
default:
|
|
600
322
|
return status;
|
|
601
323
|
}
|
|
602
324
|
};
|
|
325
|
+
//# sourceMappingURL=ClientManager.js.map
|