@webiny/api-websockets-sql 0.0.0-unstable.9f53ea597d
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/LICENSE +21 -0
- package/README.md +11 -0
- package/TableName/TableName.d.ts +6 -0
- package/TableName/TableName.js +12 -0
- package/TableName/TableName.js.map +1 -0
- package/TableName/abstractions.d.ts +7 -0
- package/TableName/abstractions.js +5 -0
- package/TableName/abstractions.js.map +1 -0
- package/WebsocketsConnectionRegistry.d.ts +24 -0
- package/WebsocketsConnectionRegistry.js +226 -0
- package/WebsocketsConnectionRegistry.js.map +1 -0
- package/index.d.ts +7 -0
- package/index.js +11 -0
- package/index.js.map +1 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Webiny
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @webiny/api-websockets-sql
|
|
2
|
+
|
|
3
|
+
> [!NOTE]
|
|
4
|
+
> This package is part of the [Webiny](https://www.webiny.com) monorepo.
|
|
5
|
+
> It’s **included in every Webiny project by default** and is not meant to be used as a standalone package.
|
|
6
|
+
|
|
7
|
+
📘 **Documentation:** [https://www.webiny.com/docs](https://www.webiny.com/docs)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
_This README file is automatically generated during the publish process._
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TableName/TableName.js","sources":["../../src/TableName/TableName.ts"],"sourcesContent":["import { TableName as TableNameAbstraction } from \"~/TableName/abstractions.js\";\n\nexport class TableName implements TableNameAbstraction.Interface {\n private readonly prefix;\n\n public constructor(prefix?: string) {\n this.prefix = prefix;\n }\n\n public resolve(name: string): string {\n if (!this.prefix) {\n return name;\n }\n return `${this.prefix}_${name}`;\n }\n}\n"],"names":["TableName","prefix","name"],"mappings":"AAEO,MAAMA;IAGT,YAAmBC,MAAe,CAAE;QAChC,IAAI,CAAC,MAAM,GAAGA;IAClB;IAEO,QAAQC,IAAY,EAAU;QACjC,IAAI,CAAC,IAAI,CAAC,MAAM,EACZ,OAAOA;QAEX,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,EAAEA,MAAM;IACnC;AACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TableName/abstractions.js","sources":["../../src/TableName/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/exports/api.js\";\n\nexport interface ITableName {\n resolve(name: string): string;\n}\n\nexport const TableName = createAbstraction<ITableName>(\"Websockets/Sql/TableName\");\n\nexport namespace TableName {\n export type Interface = ITableName;\n}\n"],"names":["TableName","createAbstraction"],"mappings":";AAMO,MAAMA,YAAYC,kBAA8B"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { KnexClient } from "@webiny/api-core-sql";
|
|
2
|
+
import { ConnectionRegistry } from "@webiny/api-websockets/exports/api.js";
|
|
3
|
+
import { TableName } from "./TableName/abstractions.js";
|
|
4
|
+
declare class WebsocketsConnectionRegistryImpl implements ConnectionRegistry.Interface {
|
|
5
|
+
private readonly knex;
|
|
6
|
+
private readonly tableName;
|
|
7
|
+
constructor(knex: KnexClient.Interface, tableName: TableName.Interface);
|
|
8
|
+
register(event: ConnectionRegistry.RegisterParams): Promise<ConnectionRegistry.Data>;
|
|
9
|
+
unregister(event: ConnectionRegistry.UnregisterParams): Promise<void>;
|
|
10
|
+
listViaConnections(connections: string[]): Promise<ConnectionRegistry.Data[]>;
|
|
11
|
+
listViaIdentity(identity: string): Promise<ConnectionRegistry.Data[]>;
|
|
12
|
+
listViaTenant(tenant: string): Promise<ConnectionRegistry.Data[]>;
|
|
13
|
+
listAll(): Promise<ConnectionRegistry.Data[]>;
|
|
14
|
+
updateLastSeen(connectionId: string): Promise<void>;
|
|
15
|
+
listStale(olderThan: Date): Promise<ConnectionRegistry.Data[]>;
|
|
16
|
+
private ensureTable;
|
|
17
|
+
private migrateTable;
|
|
18
|
+
private migrateLastSeen;
|
|
19
|
+
private toData;
|
|
20
|
+
}
|
|
21
|
+
export declare const WebsocketsConnectionRegistry: typeof WebsocketsConnectionRegistryImpl & {
|
|
22
|
+
__abstraction: import("@webiny/di").Abstraction<import("@webiny/api-websockets/features/ConnectionRegistry/abstractions").IWebsocketsConnectionRegistry>;
|
|
23
|
+
};
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { WebinyError } from "@webiny/error";
|
|
2
|
+
import { KnexClient } from "@webiny/api-core-sql";
|
|
3
|
+
import { ConnectionRegistry } from "@webiny/api-websockets/exports/api.js";
|
|
4
|
+
import { TableName } from "./TableName/abstractions.js";
|
|
5
|
+
const BASE_TABLE_NAME = "WebsocketsConnections";
|
|
6
|
+
class WebsocketsConnectionRegistryImpl {
|
|
7
|
+
constructor(knex, tableName){
|
|
8
|
+
this.knex = knex;
|
|
9
|
+
this.tableName = tableName.resolve(BASE_TABLE_NAME);
|
|
10
|
+
}
|
|
11
|
+
async register(event) {
|
|
12
|
+
try {
|
|
13
|
+
await this.ensureTable();
|
|
14
|
+
await this.migrateTable();
|
|
15
|
+
await this.migrateLastSeen();
|
|
16
|
+
const row = {
|
|
17
|
+
connectionId: event.connectionId,
|
|
18
|
+
identityId: event.identity.id,
|
|
19
|
+
identityDisplayName: event.identity.displayName,
|
|
20
|
+
identityType: event.identity.type,
|
|
21
|
+
tenant: event.tenant,
|
|
22
|
+
endpoint: event.endpoint,
|
|
23
|
+
connectedOn: event.connectedOn,
|
|
24
|
+
lastSeen: new Date().toISOString()
|
|
25
|
+
};
|
|
26
|
+
await this.knex.client(this.tableName).insert(row);
|
|
27
|
+
return this.toData(row);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
throw WebinyError.from(err, {
|
|
30
|
+
message: "Could not register websockets connection.",
|
|
31
|
+
code: "REGISTER_CONNECTION_ERROR",
|
|
32
|
+
data: {
|
|
33
|
+
event
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async unregister(event) {
|
|
39
|
+
try {
|
|
40
|
+
await this.ensureTable();
|
|
41
|
+
await this.migrateTable();
|
|
42
|
+
await this.migrateLastSeen();
|
|
43
|
+
const existing = await this.knex.client(this.tableName).where("connectionId", event.connectionId).first();
|
|
44
|
+
if (!existing) throw new WebinyError(`Connection "${event.connectionId}" does not exist.`, "CONNECTION_NOT_FOUND", {
|
|
45
|
+
connectionId: event.connectionId
|
|
46
|
+
});
|
|
47
|
+
await this.knex.client(this.tableName).where("connectionId", event.connectionId).delete();
|
|
48
|
+
} catch (err) {
|
|
49
|
+
throw WebinyError.from(err, {
|
|
50
|
+
message: "Could not unregister websockets connection.",
|
|
51
|
+
code: "UNREGISTER_CONNECTION_ERROR",
|
|
52
|
+
data: {
|
|
53
|
+
event
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async listViaConnections(connections) {
|
|
59
|
+
try {
|
|
60
|
+
await this.ensureTable();
|
|
61
|
+
await this.migrateTable();
|
|
62
|
+
await this.migrateLastSeen();
|
|
63
|
+
const rows = await this.knex.client(this.tableName).whereIn("connectionId", connections);
|
|
64
|
+
return rows.map((row)=>this.toData(row));
|
|
65
|
+
} catch (err) {
|
|
66
|
+
throw WebinyError.from(err, {
|
|
67
|
+
message: "Could not list websockets connections by connection IDs.",
|
|
68
|
+
code: "LIST_VIA_CONNECTIONS_ERROR",
|
|
69
|
+
data: {
|
|
70
|
+
connections
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async listViaIdentity(identity) {
|
|
76
|
+
try {
|
|
77
|
+
await this.ensureTable();
|
|
78
|
+
await this.migrateTable();
|
|
79
|
+
await this.migrateLastSeen();
|
|
80
|
+
const rows = await this.knex.client(this.tableName).where("identityId", identity);
|
|
81
|
+
return rows.map((row)=>this.toData(row));
|
|
82
|
+
} catch (err) {
|
|
83
|
+
throw WebinyError.from(err, {
|
|
84
|
+
message: "Could not list websockets connections by identity.",
|
|
85
|
+
code: "LIST_VIA_IDENTITY_ERROR",
|
|
86
|
+
data: {
|
|
87
|
+
identity
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async listViaTenant(tenant) {
|
|
93
|
+
try {
|
|
94
|
+
await this.ensureTable();
|
|
95
|
+
await this.migrateTable();
|
|
96
|
+
await this.migrateLastSeen();
|
|
97
|
+
const rows = await this.knex.client(this.tableName).where("tenant", tenant);
|
|
98
|
+
return rows.map((row)=>this.toData(row));
|
|
99
|
+
} catch (err) {
|
|
100
|
+
throw WebinyError.from(err, {
|
|
101
|
+
message: "Could not list websockets connections by tenant.",
|
|
102
|
+
code: "LIST_VIA_TENANT_ERROR",
|
|
103
|
+
data: {
|
|
104
|
+
tenant
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async listAll() {
|
|
110
|
+
try {
|
|
111
|
+
await this.ensureTable();
|
|
112
|
+
await this.migrateTable();
|
|
113
|
+
await this.migrateLastSeen();
|
|
114
|
+
const rows = await this.knex.client(this.tableName).select("*");
|
|
115
|
+
return rows.map((row)=>this.toData(row));
|
|
116
|
+
} catch (err) {
|
|
117
|
+
throw WebinyError.from(err, {
|
|
118
|
+
message: "Could not list all websockets connections.",
|
|
119
|
+
code: "LIST_ALL_CONNECTIONS_ERROR"
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async updateLastSeen(connectionId) {
|
|
124
|
+
try {
|
|
125
|
+
await this.ensureTable();
|
|
126
|
+
await this.migrateTable();
|
|
127
|
+
await this.migrateLastSeen();
|
|
128
|
+
await this.knex.client(this.tableName).where("connectionId", connectionId).update({
|
|
129
|
+
lastSeen: new Date().toISOString()
|
|
130
|
+
});
|
|
131
|
+
} catch (err) {
|
|
132
|
+
throw WebinyError.from(err, {
|
|
133
|
+
message: "Could not update lastSeen for websockets connection.",
|
|
134
|
+
code: "UPDATE_LAST_SEEN_ERROR",
|
|
135
|
+
data: {
|
|
136
|
+
connectionId
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async listStale(olderThan) {
|
|
142
|
+
try {
|
|
143
|
+
await this.ensureTable();
|
|
144
|
+
await this.migrateTable();
|
|
145
|
+
await this.migrateLastSeen();
|
|
146
|
+
const threshold = olderThan.toISOString();
|
|
147
|
+
const rows = await this.knex.client(this.tableName).where((qb)=>{
|
|
148
|
+
qb.where("lastSeen", "<", threshold).orWhereNull("lastSeen");
|
|
149
|
+
});
|
|
150
|
+
return rows.map((row)=>this.toData(row));
|
|
151
|
+
} catch (err) {
|
|
152
|
+
throw WebinyError.from(err, {
|
|
153
|
+
message: "Could not list stale websockets connections.",
|
|
154
|
+
code: "LIST_STALE_CONNECTIONS_ERROR",
|
|
155
|
+
data: {
|
|
156
|
+
olderThan
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async ensureTable() {
|
|
162
|
+
const exists = await this.knex.client.schema.hasTable(this.tableName);
|
|
163
|
+
if (exists) return;
|
|
164
|
+
await this.knex.client.schema.createTable(this.tableName, (table)=>{
|
|
165
|
+
table.text("connectionId").notNullable().primary();
|
|
166
|
+
table.text("identityId").notNullable();
|
|
167
|
+
table.text("identityDisplayName").notNullable();
|
|
168
|
+
table.text("identityType").notNullable();
|
|
169
|
+
table.text("tenant").notNullable();
|
|
170
|
+
table.text("endpoint").notNullable();
|
|
171
|
+
table.datetime("connectedOn").notNullable();
|
|
172
|
+
table.datetime("lastSeen").nullable();
|
|
173
|
+
table.index([
|
|
174
|
+
"identityId"
|
|
175
|
+
]);
|
|
176
|
+
table.index([
|
|
177
|
+
"tenant"
|
|
178
|
+
]);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
async migrateTable() {
|
|
182
|
+
const hasEndpoint = await this.knex.client.schema.hasColumn(this.tableName, "endpoint");
|
|
183
|
+
if (hasEndpoint) return;
|
|
184
|
+
await this.knex.client.schema.alterTable(this.tableName, (table)=>{
|
|
185
|
+
table.text("endpoint").nullable();
|
|
186
|
+
});
|
|
187
|
+
await this.knex.client(this.tableName).update({
|
|
188
|
+
endpoint: this.knex.client.raw("'https://' || \"domainName\" || '/' || \"stage\"")
|
|
189
|
+
});
|
|
190
|
+
await this.knex.client.schema.alterTable(this.tableName, (table)=>{
|
|
191
|
+
table.text("endpoint").notNullable().alter();
|
|
192
|
+
table.dropColumn("domainName");
|
|
193
|
+
table.dropColumn("stage");
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
async migrateLastSeen() {
|
|
197
|
+
const hasLastSeen = await this.knex.client.schema.hasColumn(this.tableName, "lastSeen");
|
|
198
|
+
if (hasLastSeen) return;
|
|
199
|
+
await this.knex.client.schema.alterTable(this.tableName, (table)=>{
|
|
200
|
+
table.datetime("lastSeen").nullable();
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
toData(row) {
|
|
204
|
+
return {
|
|
205
|
+
connectionId: row.connectionId,
|
|
206
|
+
identity: {
|
|
207
|
+
id: row.identityId,
|
|
208
|
+
displayName: row.identityDisplayName,
|
|
209
|
+
type: row.identityType
|
|
210
|
+
},
|
|
211
|
+
tenant: row.tenant,
|
|
212
|
+
endpoint: row.endpoint,
|
|
213
|
+
connectedOn: row.connectedOn
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const WebsocketsConnectionRegistry = ConnectionRegistry.createImplementation({
|
|
218
|
+
implementation: WebsocketsConnectionRegistryImpl,
|
|
219
|
+
dependencies: [
|
|
220
|
+
KnexClient,
|
|
221
|
+
TableName
|
|
222
|
+
]
|
|
223
|
+
});
|
|
224
|
+
export { WebsocketsConnectionRegistry };
|
|
225
|
+
|
|
226
|
+
//# sourceMappingURL=WebsocketsConnectionRegistry.js.map
|
|
@@ -0,0 +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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"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Knex } from "knex";
|
|
2
|
+
interface RegisterWebsocketsSqlStorageOperationsParams {
|
|
3
|
+
knex: Knex;
|
|
4
|
+
tableNamePrefix?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const registerWebsocketsSqlStorageOperations: (params: RegisterWebsocketsSqlStorageOperationsParams) => import("@webiny/handler").RegisterExtensionPlugin<import("@webiny/handler/types.js").Context>;
|
|
7
|
+
export {};
|
package/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createRegisterExtensionPlugin } from "@webiny/handler";
|
|
2
|
+
import { WebsocketsConnectionRegistry } from "./WebsocketsConnectionRegistry.js";
|
|
3
|
+
import { TableName } from "./TableName/abstractions.js";
|
|
4
|
+
import { TableName as TableName_js_TableName } from "./TableName/TableName.js";
|
|
5
|
+
const registerWebsocketsSqlStorageOperations = (params)=>createRegisterExtensionPlugin((context)=>{
|
|
6
|
+
context.container.registerInstance(TableName, new TableName_js_TableName(params.tableNamePrefix));
|
|
7
|
+
context.container.register(WebsocketsConnectionRegistry);
|
|
8
|
+
});
|
|
9
|
+
export { registerWebsocketsSqlStorageOperations };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
package/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import type { Knex } from \"knex\";\nimport { createRegisterExtensionPlugin } from \"@webiny/handler\";\nimport { WebsocketsConnectionRegistry } from \"./WebsocketsConnectionRegistry.js\";\nimport { TableName as TableNameAbstraction } from \"~/TableName/abstractions.js\";\nimport { TableName } from \"~/TableName/TableName.js\";\n\ninterface RegisterWebsocketsSqlStorageOperationsParams {\n knex: Knex;\n tableNamePrefix?: string;\n}\n\nexport const registerWebsocketsSqlStorageOperations = (\n params: RegisterWebsocketsSqlStorageOperationsParams\n) => {\n return createRegisterExtensionPlugin(context => {\n context.container.registerInstance(\n TableNameAbstraction,\n new TableName(params.tableNamePrefix)\n );\n context.container.register(WebsocketsConnectionRegistry);\n });\n};\n"],"names":["registerWebsocketsSqlStorageOperations","params","createRegisterExtensionPlugin","context","TableNameAbstraction","TableName","WebsocketsConnectionRegistry"],"mappings":";;;;AAWO,MAAMA,yCAAyC,CAClDC,SAEOC,8BAA8BC,CAAAA;QACjCA,QAAQ,SAAS,CAAC,gBAAgB,CAC9BC,WACA,IAAIC,uBAAUJ,OAAO,eAAe;QAExCE,QAAQ,SAAS,CAAC,QAAQ,CAACG;IAC/B"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@webiny/api-websockets-sql",
|
|
3
|
+
"version": "0.0.0-unstable.9f53ea597d",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"@webiny/api-websockets",
|
|
7
|
+
"storage-operations",
|
|
8
|
+
"sql",
|
|
9
|
+
"sau:sql"
|
|
10
|
+
],
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/webiny/webiny-js.git"
|
|
14
|
+
},
|
|
15
|
+
"description": "SQL storage operations for @webiny/api-websockets.",
|
|
16
|
+
"author": "Webiny Ltd.",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": "./index.js",
|
|
20
|
+
"./*": "./*"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@webiny/api-core-sql": "0.0.0-unstable.9f53ea597d",
|
|
24
|
+
"@webiny/api-websockets": "0.0.0-unstable.9f53ea597d",
|
|
25
|
+
"@webiny/error": "0.0.0-unstable.9f53ea597d",
|
|
26
|
+
"@webiny/feature": "0.0.0-unstable.9f53ea597d",
|
|
27
|
+
"@webiny/handler": "0.0.0-unstable.9f53ea597d",
|
|
28
|
+
"knex": "3.3.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@webiny/build-tools": "0.0.0-unstable.9f53ea597d",
|
|
32
|
+
"@webiny/di": "1.0.2",
|
|
33
|
+
"@webiny/project-utils": "0.0.0-unstable.9f53ea597d",
|
|
34
|
+
"better-sqlite3": "13.0.1",
|
|
35
|
+
"rimraf": "6.1.3",
|
|
36
|
+
"typescript": "7.0.2",
|
|
37
|
+
"vitest": "4.1.10"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"webiny": {
|
|
43
|
+
"publishFrom": "dist"
|
|
44
|
+
}
|
|
45
|
+
}
|