@webiny/api-websockets-sql 0.0.0-unstable.0d717d18dd → 0.0.0-unstable.33e4fd9647
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/WebsocketsConnectionRegistry.d.ts +3 -0
- package/WebsocketsConnectionRegistry.js +42 -33
- package/WebsocketsConnectionRegistry.js.map +1 -1
- package/package.json +10 -10
- package/toIsoString.d.ts +14 -0
- package/toIsoString.js +15 -0
- package/toIsoString.js.map +1 -0
|
@@ -13,9 +13,12 @@ declare class WebsocketsConnectionRegistryImpl implements ConnectionRegistry.Int
|
|
|
13
13
|
listAll(): Promise<ConnectionRegistry.Data[]>;
|
|
14
14
|
updateLastSeen(connectionId: string): Promise<void>;
|
|
15
15
|
listStale(olderThan: Date): Promise<ConnectionRegistry.Data[]>;
|
|
16
|
+
private prepareTable;
|
|
16
17
|
private ensureTable;
|
|
18
|
+
private createTable;
|
|
17
19
|
private migrateTable;
|
|
18
20
|
private migrateLastSeen;
|
|
21
|
+
private migrateOwner;
|
|
19
22
|
private toData;
|
|
20
23
|
}
|
|
21
24
|
export declare const WebsocketsConnectionRegistry: typeof WebsocketsConnectionRegistryImpl & {
|
|
@@ -2,7 +2,19 @@ import { WebinyError } from "@webiny/error";
|
|
|
2
2
|
import { KnexClient } from "@webiny/api-core-sql";
|
|
3
3
|
import { ConnectionRegistry } from "@webiny/api-websockets/exports/api.js";
|
|
4
4
|
import { TableName } from "./TableName/abstractions.js";
|
|
5
|
+
import { toIsoString } from "./toIsoString.js";
|
|
5
6
|
const BASE_TABLE_NAME = "WebsocketsConnections";
|
|
7
|
+
const applyOnce = async (isDone, change)=>{
|
|
8
|
+
const done = await isDone();
|
|
9
|
+
if (done) return;
|
|
10
|
+
try {
|
|
11
|
+
await change();
|
|
12
|
+
} catch (error) {
|
|
13
|
+
const doneByAnotherCall = await isDone();
|
|
14
|
+
if (doneByAnotherCall) return;
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
6
18
|
class WebsocketsConnectionRegistryImpl {
|
|
7
19
|
constructor(knex, tableName){
|
|
8
20
|
this.knex = knex;
|
|
@@ -10,9 +22,7 @@ class WebsocketsConnectionRegistryImpl {
|
|
|
10
22
|
}
|
|
11
23
|
async register(event) {
|
|
12
24
|
try {
|
|
13
|
-
await this.
|
|
14
|
-
await this.migrateTable();
|
|
15
|
-
await this.migrateLastSeen();
|
|
25
|
+
await this.prepareTable();
|
|
16
26
|
const row = {
|
|
17
27
|
connectionId: event.connectionId,
|
|
18
28
|
identityId: event.identity.id,
|
|
@@ -21,7 +31,8 @@ class WebsocketsConnectionRegistryImpl {
|
|
|
21
31
|
tenant: event.tenant,
|
|
22
32
|
endpoint: event.endpoint,
|
|
23
33
|
connectedOn: event.connectedOn,
|
|
24
|
-
lastSeen: new Date().toISOString()
|
|
34
|
+
lastSeen: new Date().toISOString(),
|
|
35
|
+
owner: event.owner ?? null
|
|
25
36
|
};
|
|
26
37
|
await this.knex.client(this.tableName).insert(row);
|
|
27
38
|
return this.toData(row);
|
|
@@ -37,9 +48,7 @@ class WebsocketsConnectionRegistryImpl {
|
|
|
37
48
|
}
|
|
38
49
|
async unregister(event) {
|
|
39
50
|
try {
|
|
40
|
-
await this.
|
|
41
|
-
await this.migrateTable();
|
|
42
|
-
await this.migrateLastSeen();
|
|
51
|
+
await this.prepareTable();
|
|
43
52
|
const existing = await this.knex.client(this.tableName).where("connectionId", event.connectionId).first();
|
|
44
53
|
if (!existing) throw new WebinyError(`Connection "${event.connectionId}" does not exist.`, "CONNECTION_NOT_FOUND", {
|
|
45
54
|
connectionId: event.connectionId
|
|
@@ -57,9 +66,7 @@ class WebsocketsConnectionRegistryImpl {
|
|
|
57
66
|
}
|
|
58
67
|
async listViaConnections(connections) {
|
|
59
68
|
try {
|
|
60
|
-
await this.
|
|
61
|
-
await this.migrateTable();
|
|
62
|
-
await this.migrateLastSeen();
|
|
69
|
+
await this.prepareTable();
|
|
63
70
|
const rows = await this.knex.client(this.tableName).whereIn("connectionId", connections);
|
|
64
71
|
return rows.map((row)=>this.toData(row));
|
|
65
72
|
} catch (err) {
|
|
@@ -74,9 +81,7 @@ class WebsocketsConnectionRegistryImpl {
|
|
|
74
81
|
}
|
|
75
82
|
async listViaIdentity(identity) {
|
|
76
83
|
try {
|
|
77
|
-
await this.
|
|
78
|
-
await this.migrateTable();
|
|
79
|
-
await this.migrateLastSeen();
|
|
84
|
+
await this.prepareTable();
|
|
80
85
|
const rows = await this.knex.client(this.tableName).where("identityId", identity);
|
|
81
86
|
return rows.map((row)=>this.toData(row));
|
|
82
87
|
} catch (err) {
|
|
@@ -91,9 +96,7 @@ class WebsocketsConnectionRegistryImpl {
|
|
|
91
96
|
}
|
|
92
97
|
async listViaTenant(tenant) {
|
|
93
98
|
try {
|
|
94
|
-
await this.
|
|
95
|
-
await this.migrateTable();
|
|
96
|
-
await this.migrateLastSeen();
|
|
99
|
+
await this.prepareTable();
|
|
97
100
|
const rows = await this.knex.client(this.tableName).where("tenant", tenant);
|
|
98
101
|
return rows.map((row)=>this.toData(row));
|
|
99
102
|
} catch (err) {
|
|
@@ -108,9 +111,7 @@ class WebsocketsConnectionRegistryImpl {
|
|
|
108
111
|
}
|
|
109
112
|
async listAll() {
|
|
110
113
|
try {
|
|
111
|
-
await this.
|
|
112
|
-
await this.migrateTable();
|
|
113
|
-
await this.migrateLastSeen();
|
|
114
|
+
await this.prepareTable();
|
|
114
115
|
const rows = await this.knex.client(this.tableName).select("*");
|
|
115
116
|
return rows.map((row)=>this.toData(row));
|
|
116
117
|
} catch (err) {
|
|
@@ -122,9 +123,7 @@ class WebsocketsConnectionRegistryImpl {
|
|
|
122
123
|
}
|
|
123
124
|
async updateLastSeen(connectionId) {
|
|
124
125
|
try {
|
|
125
|
-
await this.
|
|
126
|
-
await this.migrateTable();
|
|
127
|
-
await this.migrateLastSeen();
|
|
126
|
+
await this.prepareTable();
|
|
128
127
|
await this.knex.client(this.tableName).where("connectionId", connectionId).update({
|
|
129
128
|
lastSeen: new Date().toISOString()
|
|
130
129
|
});
|
|
@@ -140,9 +139,7 @@ class WebsocketsConnectionRegistryImpl {
|
|
|
140
139
|
}
|
|
141
140
|
async listStale(olderThan) {
|
|
142
141
|
try {
|
|
143
|
-
await this.
|
|
144
|
-
await this.migrateTable();
|
|
145
|
-
await this.migrateLastSeen();
|
|
142
|
+
await this.prepareTable();
|
|
146
143
|
const threshold = olderThan.toISOString();
|
|
147
144
|
const rows = await this.knex.client(this.tableName).where((qb)=>{
|
|
148
145
|
qb.where("lastSeen", "<", threshold).orWhereNull("lastSeen");
|
|
@@ -158,9 +155,16 @@ class WebsocketsConnectionRegistryImpl {
|
|
|
158
155
|
});
|
|
159
156
|
}
|
|
160
157
|
}
|
|
158
|
+
async prepareTable() {
|
|
159
|
+
await this.ensureTable();
|
|
160
|
+
await this.migrateTable();
|
|
161
|
+
await this.migrateLastSeen();
|
|
162
|
+
await this.migrateOwner();
|
|
163
|
+
}
|
|
161
164
|
async ensureTable() {
|
|
162
|
-
|
|
163
|
-
|
|
165
|
+
await applyOnce(()=>this.knex.client.schema.hasTable(this.tableName), ()=>this.createTable());
|
|
166
|
+
}
|
|
167
|
+
async createTable() {
|
|
164
168
|
await this.knex.client.schema.createTable(this.tableName, (table)=>{
|
|
165
169
|
table.text("connectionId").notNullable().primary();
|
|
166
170
|
table.text("identityId").notNullable();
|
|
@@ -170,6 +174,7 @@ class WebsocketsConnectionRegistryImpl {
|
|
|
170
174
|
table.text("endpoint").notNullable();
|
|
171
175
|
table.datetime("connectedOn").notNullable();
|
|
172
176
|
table.datetime("lastSeen").nullable();
|
|
177
|
+
table.text("owner").nullable();
|
|
173
178
|
table.index([
|
|
174
179
|
"identityId"
|
|
175
180
|
]);
|
|
@@ -194,11 +199,14 @@ class WebsocketsConnectionRegistryImpl {
|
|
|
194
199
|
});
|
|
195
200
|
}
|
|
196
201
|
async migrateLastSeen() {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
+
await applyOnce(()=>this.knex.client.schema.hasColumn(this.tableName, "lastSeen"), ()=>this.knex.client.schema.alterTable(this.tableName, (table)=>{
|
|
203
|
+
table.datetime("lastSeen").nullable();
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
async migrateOwner() {
|
|
207
|
+
await applyOnce(()=>this.knex.client.schema.hasColumn(this.tableName, "owner"), ()=>this.knex.client.schema.alterTable(this.tableName, (table)=>{
|
|
208
|
+
table.text("owner").nullable();
|
|
209
|
+
}));
|
|
202
210
|
}
|
|
203
211
|
toData(row) {
|
|
204
212
|
return {
|
|
@@ -210,7 +218,8 @@ class WebsocketsConnectionRegistryImpl {
|
|
|
210
218
|
},
|
|
211
219
|
tenant: row.tenant,
|
|
212
220
|
endpoint: row.endpoint,
|
|
213
|
-
connectedOn: row.connectedOn
|
|
221
|
+
connectedOn: toIsoString(row.connectedOn),
|
|
222
|
+
owner: row.owner ?? void 0
|
|
214
223
|
};
|
|
215
224
|
}
|
|
216
225
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebsocketsConnectionRegistry.js","sources":["../src/WebsocketsConnectionRegistry.ts"],"sourcesContent":["import { WebinyError } from \"@webiny/error\";\nimport { KnexClient } from \"@webiny/api-core-sql\";\nimport { ConnectionRegistry } from \"@webiny/api-websockets/exports/api.js\";\nimport { TableName } from \"~/TableName/abstractions.js\";\n\ninterface ConnectionRow {\n connectionId: string;\n identityId: string;\n identityDisplayName: string;\n identityType: string;\n tenant: string;\n endpoint: string;\n connectedOn: string;\n lastSeen: string | null;\n}\n\nconst BASE_TABLE_NAME = \"WebsocketsConnections\";\n\nclass WebsocketsConnectionRegistryImpl implements ConnectionRegistry.Interface {\n private readonly knex;\n private readonly tableName;\n\n public constructor(knex: KnexClient.Interface, tableName: TableName.Interface) {\n this.knex = knex;\n this.tableName = tableName.resolve(BASE_TABLE_NAME);\n }\n\n public async register(\n event: ConnectionRegistry.RegisterParams\n ): Promise<ConnectionRegistry.Data> {\n try {\n await this.ensureTable();\n await this.migrateTable();\n await this.migrateLastSeen();\n\n const row: ConnectionRow = {\n connectionId: event.connectionId,\n identityId: event.identity.id,\n identityDisplayName: event.identity.displayName,\n identityType: event.identity.type,\n tenant: event.tenant,\n endpoint: event.endpoint,\n connectedOn: event.connectedOn,\n lastSeen: new Date().toISOString()\n };\n\n await this.knex.client<ConnectionRow>(this.tableName).insert(row);\n\n return this.toData(row);\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not register websockets connection.\",\n code: \"REGISTER_CONNECTION_ERROR\",\n data: { event }\n });\n }\n }\n\n public async unregister(event: ConnectionRegistry.UnregisterParams): Promise<void> {\n try {\n await this.ensureTable();\n await this.migrateTable();\n await this.migrateLastSeen();\n\n const existing = await this.knex\n .client<ConnectionRow>(this.tableName)\n .where(\"connectionId\", event.connectionId)\n .first();\n\n if (!existing) {\n throw new WebinyError(\n `Connection \"${event.connectionId}\" does not exist.`,\n \"CONNECTION_NOT_FOUND\",\n { connectionId: event.connectionId }\n );\n }\n\n await this.knex\n .client<ConnectionRow>(this.tableName)\n .where(\"connectionId\", event.connectionId)\n .delete();\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not unregister websockets connection.\",\n code: \"UNREGISTER_CONNECTION_ERROR\",\n data: { event }\n });\n }\n }\n\n public async listViaConnections(connections: string[]): Promise<ConnectionRegistry.Data[]> {\n try {\n await this.ensureTable();\n await this.migrateTable();\n await this.migrateLastSeen();\n\n const rows = await this.knex\n .client<ConnectionRow>(this.tableName)\n .whereIn(\"connectionId\", connections);\n\n return rows.map(row => this.toData(row));\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not list websockets connections by connection IDs.\",\n code: \"LIST_VIA_CONNECTIONS_ERROR\",\n data: { connections }\n });\n }\n }\n\n public async listViaIdentity(identity: string): Promise<ConnectionRegistry.Data[]> {\n try {\n await this.ensureTable();\n await this.migrateTable();\n await this.migrateLastSeen();\n\n const rows = await this.knex\n .client<ConnectionRow>(this.tableName)\n .where(\"identityId\", identity);\n\n return rows.map(row => this.toData(row));\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not list websockets connections by identity.\",\n code: \"LIST_VIA_IDENTITY_ERROR\",\n data: { identity }\n });\n }\n }\n\n public async listViaTenant(tenant: string): Promise<ConnectionRegistry.Data[]> {\n try {\n await this.ensureTable();\n await this.migrateTable();\n await this.migrateLastSeen();\n\n const rows = await this.knex\n .client<ConnectionRow>(this.tableName)\n .where(\"tenant\", tenant);\n\n return rows.map(row => this.toData(row));\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not list websockets connections by tenant.\",\n code: \"LIST_VIA_TENANT_ERROR\",\n data: { tenant }\n });\n }\n }\n\n public async listAll(): Promise<ConnectionRegistry.Data[]> {\n try {\n await this.ensureTable();\n await this.migrateTable();\n await this.migrateLastSeen();\n\n const rows = await this.knex.client<ConnectionRow>(this.tableName).select(\"*\");\n\n return rows.map(row => this.toData(row));\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not list all websockets connections.\",\n code: \"LIST_ALL_CONNECTIONS_ERROR\"\n });\n }\n }\n\n public async updateLastSeen(connectionId: string): Promise<void> {\n try {\n await this.ensureTable();\n await this.migrateTable();\n await this.migrateLastSeen();\n\n await this.knex\n .client<ConnectionRow>(this.tableName)\n .where(\"connectionId\", connectionId)\n .update({ lastSeen: new Date().toISOString() });\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not update lastSeen for websockets connection.\",\n code: \"UPDATE_LAST_SEEN_ERROR\",\n data: { connectionId }\n });\n }\n }\n\n public async listStale(olderThan: Date): Promise<ConnectionRegistry.Data[]> {\n try {\n await this.ensureTable();\n await this.migrateTable();\n await this.migrateLastSeen();\n\n const threshold = olderThan.toISOString();\n\n const rows = await this.knex.client<ConnectionRow>(this.tableName).where(qb => {\n qb.where(\"lastSeen\", \"<\", threshold).orWhereNull(\"lastSeen\");\n });\n\n return rows.map(row => this.toData(row));\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not list stale websockets connections.\",\n code: \"LIST_STALE_CONNECTIONS_ERROR\",\n data: { olderThan }\n });\n }\n }\n\n private async ensureTable(): Promise<void> {\n const exists = await this.knex.client.schema.hasTable(this.tableName);\n if (exists) {\n return;\n }\n\n await this.knex.client.schema.createTable(this.tableName, table => {\n table.text(\"connectionId\").notNullable().primary();\n table.text(\"identityId\").notNullable();\n table.text(\"identityDisplayName\").notNullable();\n table.text(\"identityType\").notNullable();\n table.text(\"tenant\").notNullable();\n table.text(\"endpoint\").notNullable();\n table.datetime(\"connectedOn\").notNullable();\n table.datetime(\"lastSeen\").nullable();\n table.index([\"identityId\"]);\n table.index([\"tenant\"]);\n });\n }\n\n private async migrateTable(): Promise<void> {\n const hasEndpoint = await this.knex.client.schema.hasColumn(this.tableName, \"endpoint\");\n if (hasEndpoint) {\n return;\n }\n\n await this.knex.client.schema.alterTable(this.tableName, table => {\n table.text(\"endpoint\").nullable();\n });\n\n await this.knex.client(this.tableName).update({\n endpoint: this.knex.client.raw(\"'https://' || \\\"domainName\\\" || '/' || \\\"stage\\\"\")\n });\n\n await this.knex.client.schema.alterTable(this.tableName, table => {\n table.text(\"endpoint\").notNullable().alter();\n table.dropColumn(\"domainName\");\n table.dropColumn(\"stage\");\n });\n }\n\n private async migrateLastSeen(): Promise<void> {\n const hasLastSeen = await this.knex.client.schema.hasColumn(this.tableName, \"lastSeen\");\n if (hasLastSeen) {\n return;\n }\n\n await this.knex.client.schema.alterTable(this.tableName, table => {\n table.datetime(\"lastSeen\").nullable();\n });\n }\n\n private toData(row: ConnectionRow): ConnectionRegistry.Data {\n return {\n connectionId: row.connectionId,\n identity: {\n id: row.identityId,\n displayName: row.identityDisplayName,\n type: row.identityType\n },\n tenant: row.tenant,\n endpoint: row.endpoint,\n connectedOn: row.connectedOn\n };\n }\n}\n\nexport const WebsocketsConnectionRegistry = ConnectionRegistry.createImplementation({\n implementation: WebsocketsConnectionRegistryImpl,\n dependencies: [KnexClient, TableName]\n});\n"],"names":["BASE_TABLE_NAME","WebsocketsConnectionRegistryImpl","knex","tableName","event","row","Date","err","WebinyError","existing","connections","rows","identity","tenant","connectionId","olderThan","threshold","qb","exists","table","hasEndpoint","hasLastSeen","WebsocketsConnectionRegistry","ConnectionRegistry","KnexClient","TableName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WebsocketsConnectionRegistry.js","sources":["../src/WebsocketsConnectionRegistry.ts"],"sourcesContent":["import { WebinyError } from \"@webiny/error\";\nimport { KnexClient } from \"@webiny/api-core-sql\";\nimport { ConnectionRegistry } from \"@webiny/api-websockets/exports/api.js\";\nimport { TableName } from \"~/TableName/abstractions.js\";\nimport { toIsoString } from \"~/toIsoString.js\";\n\ninterface ConnectionRow {\n connectionId: string;\n identityId: string;\n identityDisplayName: string;\n identityType: string;\n tenant: string;\n endpoint: string;\n connectedOn: string;\n lastSeen: string | null;\n owner: string | null;\n}\n\nconst BASE_TABLE_NAME = \"WebsocketsConnections\";\n\n/*\n * Runs `change` unless `isDone` already holds. After an upgrade every browser reconnects at once, so\n * several calls, in one process or in several, can find the same table or column missing; all but\n * the first then fail because it exists by now. A failure after which `isDone` holds was one of\n * those and is not an error.\n */\nconst applyOnce = async (\n isDone: () => Promise<boolean>,\n change: () => Promise<unknown>\n): Promise<void> => {\n const done = await isDone();\n if (done) {\n return;\n }\n try {\n await change();\n } catch (error) {\n const doneByAnotherCall = await isDone();\n if (doneByAnotherCall) {\n return;\n }\n throw error;\n }\n};\n\nclass WebsocketsConnectionRegistryImpl implements ConnectionRegistry.Interface {\n private readonly knex;\n private readonly tableName;\n\n public constructor(knex: KnexClient.Interface, tableName: TableName.Interface) {\n this.knex = knex;\n this.tableName = tableName.resolve(BASE_TABLE_NAME);\n }\n\n public async register(\n event: ConnectionRegistry.RegisterParams\n ): Promise<ConnectionRegistry.Data> {\n try {\n await this.prepareTable();\n\n const row: ConnectionRow = {\n connectionId: event.connectionId,\n identityId: event.identity.id,\n identityDisplayName: event.identity.displayName,\n identityType: event.identity.type,\n tenant: event.tenant,\n endpoint: event.endpoint,\n connectedOn: event.connectedOn,\n lastSeen: new Date().toISOString(),\n owner: event.owner ?? null\n };\n\n await this.knex.client<ConnectionRow>(this.tableName).insert(row);\n\n return this.toData(row);\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not register websockets connection.\",\n code: \"REGISTER_CONNECTION_ERROR\",\n data: { event }\n });\n }\n }\n\n public async unregister(event: ConnectionRegistry.UnregisterParams): Promise<void> {\n try {\n await this.prepareTable();\n\n const existing = await this.knex\n .client<ConnectionRow>(this.tableName)\n .where(\"connectionId\", event.connectionId)\n .first();\n\n if (!existing) {\n throw new WebinyError(\n `Connection \"${event.connectionId}\" does not exist.`,\n \"CONNECTION_NOT_FOUND\",\n { connectionId: event.connectionId }\n );\n }\n\n await this.knex\n .client<ConnectionRow>(this.tableName)\n .where(\"connectionId\", event.connectionId)\n .delete();\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not unregister websockets connection.\",\n code: \"UNREGISTER_CONNECTION_ERROR\",\n data: { event }\n });\n }\n }\n\n public async listViaConnections(connections: string[]): Promise<ConnectionRegistry.Data[]> {\n try {\n await this.prepareTable();\n\n const rows = await this.knex\n .client<ConnectionRow>(this.tableName)\n .whereIn(\"connectionId\", connections);\n\n return rows.map(row => this.toData(row));\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not list websockets connections by connection IDs.\",\n code: \"LIST_VIA_CONNECTIONS_ERROR\",\n data: { connections }\n });\n }\n }\n\n public async listViaIdentity(identity: string): Promise<ConnectionRegistry.Data[]> {\n try {\n await this.prepareTable();\n\n const rows = await this.knex\n .client<ConnectionRow>(this.tableName)\n .where(\"identityId\", identity);\n\n return rows.map(row => this.toData(row));\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not list websockets connections by identity.\",\n code: \"LIST_VIA_IDENTITY_ERROR\",\n data: { identity }\n });\n }\n }\n\n public async listViaTenant(tenant: string): Promise<ConnectionRegistry.Data[]> {\n try {\n await this.prepareTable();\n\n const rows = await this.knex\n .client<ConnectionRow>(this.tableName)\n .where(\"tenant\", tenant);\n\n return rows.map(row => this.toData(row));\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not list websockets connections by tenant.\",\n code: \"LIST_VIA_TENANT_ERROR\",\n data: { tenant }\n });\n }\n }\n\n public async listAll(): Promise<ConnectionRegistry.Data[]> {\n try {\n await this.prepareTable();\n\n const rows = await this.knex.client<ConnectionRow>(this.tableName).select(\"*\");\n\n return rows.map(row => this.toData(row));\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not list all websockets connections.\",\n code: \"LIST_ALL_CONNECTIONS_ERROR\"\n });\n }\n }\n\n public async updateLastSeen(connectionId: string): Promise<void> {\n try {\n await this.prepareTable();\n\n await this.knex\n .client<ConnectionRow>(this.tableName)\n .where(\"connectionId\", connectionId)\n .update({ lastSeen: new Date().toISOString() });\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not update lastSeen for websockets connection.\",\n code: \"UPDATE_LAST_SEEN_ERROR\",\n data: { connectionId }\n });\n }\n }\n\n public async listStale(olderThan: Date): Promise<ConnectionRegistry.Data[]> {\n try {\n await this.prepareTable();\n\n const threshold = olderThan.toISOString();\n\n const rows = await this.knex.client<ConnectionRow>(this.tableName).where(qb => {\n qb.where(\"lastSeen\", \"<\", threshold).orWhereNull(\"lastSeen\");\n });\n\n return rows.map(row => this.toData(row));\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not list stale websockets connections.\",\n code: \"LIST_STALE_CONNECTIONS_ERROR\",\n data: { olderThan }\n });\n }\n }\n\n private async prepareTable(): Promise<void> {\n await this.ensureTable();\n await this.migrateTable();\n await this.migrateLastSeen();\n await this.migrateOwner();\n }\n\n /*\n * `schema` is read fresh in each call below: it is a new builder on every read, and one builder\n * reused would run the statements queued on it before.\n */\n private async ensureTable(): Promise<void> {\n await applyOnce(\n () => this.knex.client.schema.hasTable(this.tableName),\n () => this.createTable()\n );\n }\n\n private async createTable(): Promise<void> {\n await this.knex.client.schema.createTable(this.tableName, table => {\n table.text(\"connectionId\").notNullable().primary();\n table.text(\"identityId\").notNullable();\n table.text(\"identityDisplayName\").notNullable();\n table.text(\"identityType\").notNullable();\n table.text(\"tenant\").notNullable();\n table.text(\"endpoint\").notNullable();\n table.datetime(\"connectedOn\").notNullable();\n table.datetime(\"lastSeen\").nullable();\n table.text(\"owner\").nullable();\n table.index([\"identityId\"]);\n table.index([\"tenant\"]);\n });\n }\n\n private async migrateTable(): Promise<void> {\n const hasEndpoint = await this.knex.client.schema.hasColumn(this.tableName, \"endpoint\");\n if (hasEndpoint) {\n return;\n }\n\n await this.knex.client.schema.alterTable(this.tableName, table => {\n table.text(\"endpoint\").nullable();\n });\n\n await this.knex.client(this.tableName).update({\n endpoint: this.knex.client.raw(\"'https://' || \\\"domainName\\\" || '/' || \\\"stage\\\"\")\n });\n\n await this.knex.client.schema.alterTable(this.tableName, table => {\n table.text(\"endpoint\").notNullable().alter();\n table.dropColumn(\"domainName\");\n table.dropColumn(\"stage\");\n });\n }\n\n private async migrateLastSeen(): Promise<void> {\n await applyOnce(\n () => this.knex.client.schema.hasColumn(this.tableName, \"lastSeen\"),\n () => {\n return this.knex.client.schema.alterTable(this.tableName, table => {\n table.datetime(\"lastSeen\").nullable();\n });\n }\n );\n }\n\n private async migrateOwner(): Promise<void> {\n await applyOnce(\n () => this.knex.client.schema.hasColumn(this.tableName, \"owner\"),\n () => {\n return this.knex.client.schema.alterTable(this.tableName, table => {\n table.text(\"owner\").nullable();\n });\n }\n );\n }\n\n private toData(row: ConnectionRow): ConnectionRegistry.Data {\n return {\n connectionId: row.connectionId,\n identity: {\n id: row.identityId,\n displayName: row.identityDisplayName,\n type: row.identityType\n },\n tenant: row.tenant,\n endpoint: row.endpoint,\n connectedOn: toIsoString(row.connectedOn),\n owner: row.owner ?? undefined\n };\n }\n}\n\nexport const WebsocketsConnectionRegistry = ConnectionRegistry.createImplementation({\n implementation: WebsocketsConnectionRegistryImpl,\n dependencies: [KnexClient, TableName]\n});\n"],"names":["BASE_TABLE_NAME","applyOnce","isDone","change","done","error","doneByAnotherCall","WebsocketsConnectionRegistryImpl","knex","tableName","event","row","Date","err","WebinyError","existing","connections","rows","identity","tenant","connectionId","olderThan","threshold","qb","table","hasEndpoint","toIsoString","undefined","WebsocketsConnectionRegistry","ConnectionRegistry","KnexClient","TableName"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webiny/api-websockets-sql",
|
|
3
|
-
"version": "0.0.0-unstable.
|
|
3
|
+
"version": "0.0.0-unstable.33e4fd9647",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"@webiny/api-websockets",
|
|
@@ -20,20 +20,20 @@
|
|
|
20
20
|
"./*": "./*"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@webiny/api-core-sql": "0.0.0-unstable.
|
|
24
|
-
"@webiny/api-websockets": "0.0.0-unstable.
|
|
25
|
-
"@webiny/error": "0.0.0-unstable.
|
|
26
|
-
"@webiny/feature": "0.0.0-unstable.
|
|
23
|
+
"@webiny/api-core-sql": "0.0.0-unstable.33e4fd9647",
|
|
24
|
+
"@webiny/api-websockets": "0.0.0-unstable.33e4fd9647",
|
|
25
|
+
"@webiny/error": "0.0.0-unstable.33e4fd9647",
|
|
26
|
+
"@webiny/feature": "0.0.0-unstable.33e4fd9647",
|
|
27
27
|
"knex": "3.3.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@webiny/
|
|
31
|
-
"@webiny/
|
|
32
|
-
"@webiny/
|
|
33
|
-
"better-sqlite3": "13.0.
|
|
30
|
+
"@webiny/api-core": "0.0.0-unstable.33e4fd9647",
|
|
31
|
+
"@webiny/build-tools": "0.0.0-unstable.33e4fd9647",
|
|
32
|
+
"@webiny/di": "2.0.0",
|
|
33
|
+
"better-sqlite3": "13.0.3",
|
|
34
34
|
"rimraf": "6.1.3",
|
|
35
35
|
"typescript": "7.0.2",
|
|
36
|
-
"vitest": "4.1.
|
|
36
|
+
"vitest": "4.1.11"
|
|
37
37
|
},
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
package/toIsoString.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize a timestamp read out of a `datetime` column to a UTC ISO string.
|
|
3
|
+
*
|
|
4
|
+
* We always write ISO strings, but the column's read shape is the driver's decision: node-postgres
|
|
5
|
+
* and mysql2 parse it into a `Date`, better-sqlite3 hands back the text it was given, and some
|
|
6
|
+
* configurations return "2026-08-04 17:02:06" with no `T` and no zone. That wall-clock is UTC,
|
|
7
|
+
* because that is how it was written, so the space form is read back as UTC.
|
|
8
|
+
*
|
|
9
|
+
* Taking `unknown` keeps the variance out of the row type, so the rest of the registry can treat a
|
|
10
|
+
* timestamp as the `string` it is declared to be. Anything unparseable throws rather than being
|
|
11
|
+
* stringified: these values are compared against a cutoff to decide which connections are still
|
|
12
|
+
* live, and a quietly malformed one drops a user's connections with no error to show for it.
|
|
13
|
+
*/
|
|
14
|
+
export declare const toIsoString: (value: unknown) => string;
|
package/toIsoString.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { WebinyError } from "@webiny/error";
|
|
2
|
+
const toIsoString = (value)=>{
|
|
3
|
+
if (value instanceof Date) return value.toISOString();
|
|
4
|
+
if ("string" == typeof value) {
|
|
5
|
+
const normalized = value.includes(" ") && !value.includes("T") ? `${value.replace(" ", "T")}Z` : value;
|
|
6
|
+
const parsed = new Date(normalized);
|
|
7
|
+
if (!Number.isNaN(parsed.getTime())) return parsed.toISOString();
|
|
8
|
+
}
|
|
9
|
+
throw new WebinyError("Could not read a websockets connection timestamp.", "INVALID_CONNECTION_TIMESTAMP", {
|
|
10
|
+
value
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
export { toIsoString };
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=toIsoString.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toIsoString.js","sources":["../src/toIsoString.ts"],"sourcesContent":["import { WebinyError } from \"@webiny/error\";\n\n/**\n * Normalize a timestamp read out of a `datetime` column to a UTC ISO string.\n *\n * We always write ISO strings, but the column's read shape is the driver's decision: node-postgres\n * and mysql2 parse it into a `Date`, better-sqlite3 hands back the text it was given, and some\n * configurations return \"2026-08-04 17:02:06\" with no `T` and no zone. That wall-clock is UTC,\n * because that is how it was written, so the space form is read back as UTC.\n *\n * Taking `unknown` keeps the variance out of the row type, so the rest of the registry can treat a\n * timestamp as the `string` it is declared to be. Anything unparseable throws rather than being\n * stringified: these values are compared against a cutoff to decide which connections are still\n * live, and a quietly malformed one drops a user's connections with no error to show for it.\n */\nexport const toIsoString = (value: unknown): string => {\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n if (typeof value === \"string\") {\n const normalized =\n value.includes(\" \") && !value.includes(\"T\") ? `${value.replace(\" \", \"T\")}Z` : value;\n\n const parsed = new Date(normalized);\n if (!Number.isNaN(parsed.getTime())) {\n return parsed.toISOString();\n }\n }\n\n throw new WebinyError(\n \"Could not read a websockets connection timestamp.\",\n \"INVALID_CONNECTION_TIMESTAMP\",\n { value }\n );\n};\n"],"names":["toIsoString","value","Date","normalized","parsed","Number","WebinyError"],"mappings":";AAeO,MAAMA,cAAc,CAACC;IACxB,IAAIA,iBAAiBC,MACjB,OAAOD,MAAM,WAAW;IAG5B,IAAI,AAAiB,YAAjB,OAAOA,OAAoB;QAC3B,MAAME,aACFF,MAAM,QAAQ,CAAC,QAAQ,CAACA,MAAM,QAAQ,CAAC,OAAO,GAAGA,MAAM,OAAO,CAAC,KAAK,KAAK,CAAC,CAAC,GAAGA;QAElF,MAAMG,SAAS,IAAIF,KAAKC;QACxB,IAAI,CAACE,OAAO,KAAK,CAACD,OAAO,OAAO,KAC5B,OAAOA,OAAO,WAAW;IAEjC;IAEA,MAAM,IAAIE,YACN,qDACA,gCACA;QAAEL;IAAM;AAEhB"}
|