@verdant-web/server 3.3.11 → 4.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/bin/server.mjs +110 -107
- package/dist/esm/TokenVerifier.d.ts +1 -0
- package/dist/esm/TokenVerifier.js +3 -0
- package/dist/esm/TokenVerifier.js.map +1 -1
- package/dist/esm/adapters/node/errorHandler.d.ts +2 -0
- package/dist/esm/adapters/node/errorHandler.js +12 -0
- package/dist/esm/adapters/node/errorHandler.js.map +1 -0
- package/dist/esm/adapters/node/getRequestToken.d.ts +1 -0
- package/dist/esm/adapters/node/getRequestToken.js +25 -0
- package/dist/esm/adapters/node/getRequestToken.js.map +1 -0
- package/dist/esm/adapters/node/httpRouter.d.ts +47 -0
- package/dist/esm/adapters/node/httpRouter.js +88 -0
- package/dist/esm/adapters/node/httpRouter.js.map +1 -0
- package/dist/esm/adapters/node/index.d.ts +5 -0
- package/dist/esm/adapters/node/index.js +6 -0
- package/dist/esm/adapters/node/index.js.map +1 -0
- package/dist/esm/adapters/node/tokenMiddleware.d.ts +7 -0
- package/dist/esm/adapters/node/tokenMiddleware.js +22 -0
- package/dist/esm/adapters/node/tokenMiddleware.js.map +1 -0
- package/dist/esm/adapters/node/websocketHandler.d.ts +4 -0
- package/dist/esm/adapters/node/websocketHandler.js +54 -0
- package/dist/esm/adapters/node/websocketHandler.js.map +1 -0
- package/dist/esm/connections/ClientConnection.d.ts +25 -0
- package/dist/esm/{ClientConnection.js → connections/ClientConnection.js} +47 -38
- package/dist/esm/connections/ClientConnection.js.map +1 -0
- package/dist/esm/connections/MessageSender.d.ts +5 -0
- package/dist/esm/connections/MessageSender.js.map +1 -0
- package/dist/esm/connections/Presence.d.ts +23 -0
- package/dist/esm/connections/Presence.js +65 -0
- package/dist/esm/connections/Presence.js.map +1 -0
- package/dist/esm/core.d.ts +15 -0
- package/dist/esm/core.js +15 -0
- package/dist/esm/core.js.map +1 -0
- package/dist/esm/defaults.d.ts +2 -0
- package/dist/esm/defaults.js +3 -0
- package/dist/esm/defaults.js.map +1 -0
- package/dist/esm/files/FileStorage.d.ts +12 -3
- package/dist/esm/files/FileStorage.js +20 -0
- package/dist/esm/files/FileStorage.js.map +1 -1
- package/dist/esm/index.d.ts +7 -7
- package/dist/esm/index.js +2 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internals.d.ts +14 -0
- package/dist/esm/internals.js +12 -0
- package/dist/esm/internals.js.map +1 -0
- package/dist/esm/libraries/Library.d.ts +78 -0
- package/dist/esm/{ServerLibrary.js → libraries/Library.js} +153 -144
- package/dist/esm/libraries/Library.js.map +1 -0
- package/dist/esm/{ServerLibrary.test.js → libraries/Library.test.js} +42 -33
- package/dist/esm/libraries/Library.test.js.map +1 -0
- package/dist/esm/libraries/generic.d.ts +8 -0
- package/dist/esm/libraries/generic.js +2 -0
- package/dist/esm/libraries/generic.js.map +1 -0
- package/dist/esm/libraries/singleNode.d.ts +53 -0
- package/dist/esm/libraries/singleNode.js +88 -0
- package/dist/esm/libraries/singleNode.js.map +1 -0
- package/dist/esm/logger.d.ts +1 -0
- package/dist/esm/logger.js +2 -0
- package/dist/esm/logger.js.map +1 -0
- package/dist/esm/storage/Storage.d.ts +39 -38
- package/dist/esm/storage/StorageManager.d.ts +11 -0
- package/dist/esm/storage/StorageManager.js +32 -0
- package/dist/esm/storage/StorageManager.js.map +1 -0
- package/dist/esm/storage/index.d.ts +1 -2
- package/dist/esm/storage/index.js +1 -2
- package/dist/esm/storage/index.js.map +1 -1
- package/dist/esm/storage/sqlShard/Databases.d.ts +5 -3
- package/dist/esm/storage/sqlShard/Databases.js +11 -7
- package/dist/esm/storage/sqlShard/Databases.js.map +1 -1
- package/dist/esm/storage/sqlShard/SqlBaselines.d.ts +16 -17
- package/dist/esm/storage/sqlShard/SqlBaselines.js +55 -59
- package/dist/esm/storage/sqlShard/SqlBaselines.js.map +1 -1
- package/dist/esm/storage/sqlShard/SqlFileMetadata.d.ts +13 -13
- package/dist/esm/storage/sqlShard/SqlFileMetadata.js +44 -57
- package/dist/esm/storage/sqlShard/SqlFileMetadata.js.map +1 -1
- package/dist/esm/storage/sqlShard/SqlOperations.d.ts +12 -12
- package/dist/esm/storage/sqlShard/SqlOperations.js +38 -73
- package/dist/esm/storage/sqlShard/SqlOperations.js.map +1 -1
- package/dist/esm/storage/sqlShard/SqlReplicas.d.ts +18 -17
- package/dist/esm/storage/sqlShard/SqlReplicas.js +63 -100
- package/dist/esm/storage/sqlShard/SqlReplicas.js.map +1 -1
- package/dist/esm/storage/sqlShard/database.d.ts +25 -5
- package/dist/esm/storage/sqlShard/database.js +47 -15
- package/dist/esm/storage/sqlShard/database.js.map +1 -1
- package/dist/esm/storage/sqlShard/migrations/v1.d.ts +2 -3
- package/dist/esm/storage/sqlShard/migrations/v1.js +42 -47
- package/dist/esm/storage/sqlShard/migrations/v1.js.map +1 -1
- package/dist/esm/storage/sqlShard/migrations/v2.d.ts +2 -3
- package/dist/esm/storage/sqlShard/migrations/v2.js +5 -15
- package/dist/esm/storage/sqlShard/migrations/v2.js.map +1 -1
- package/dist/esm/storage/sqlShard/migrations.d.ts +5 -7
- package/dist/esm/storage/sqlShard/migrations.js +48 -1
- package/dist/esm/storage/sqlShard/migrations.js.map +1 -1
- package/dist/esm/storage/sqlShard/sqlShardStorage.d.ts +25 -4
- package/dist/esm/storage/sqlShard/sqlShardStorage.js +43 -40
- package/dist/esm/storage/sqlShard/sqlShardStorage.js.map +1 -1
- package/dist/esm/storage/sqlShard/tables.d.ts +9 -6
- package/dist/esm/types.d.ts +1 -2
- package/package.json +24 -13
- package/src/TokenVerifier.ts +4 -0
- package/src/adapters/node/errorHandler.ts +18 -0
- package/src/adapters/node/getRequestToken.ts +34 -0
- package/src/adapters/node/httpRouter.ts +139 -0
- package/src/adapters/node/index.ts +5 -0
- package/src/adapters/node/tokenMiddleware.ts +32 -0
- package/src/adapters/node/websocketHandler.ts +79 -0
- package/src/{ClientConnection.ts → connections/ClientConnection.ts} +64 -57
- package/src/connections/MessageSender.ts +6 -0
- package/src/connections/Presence.ts +95 -0
- package/src/core.ts +36 -0
- package/src/defaults.ts +2 -0
- package/src/files/FileStorage.ts +36 -3
- package/src/index.ts +7 -7
- package/src/internals.ts +14 -0
- package/src/{ServerLibrary.test.ts → libraries/Library.test.ts} +51 -49
- package/src/{ServerLibrary.ts → libraries/Library.ts} +203 -206
- package/src/libraries/generic.ts +9 -0
- package/src/libraries/singleNode.ts +145 -0
- package/src/logger.ts +4 -0
- package/src/storage/Storage.ts +37 -66
- package/src/storage/StorageManager.ts +39 -0
- package/src/storage/index.ts +1 -2
- package/src/storage/sqlShard/Databases.ts +26 -13
- package/src/storage/sqlShard/SqlBaselines.ts +58 -67
- package/src/storage/sqlShard/SqlFileMetadata.ts +51 -72
- package/src/storage/sqlShard/SqlOperations.ts +57 -81
- package/src/storage/sqlShard/SqlReplicas.ts +80 -121
- package/src/storage/sqlShard/database.ts +82 -18
- package/src/storage/sqlShard/migrations/v1.ts +42 -52
- package/src/storage/sqlShard/migrations/v2.ts +5 -19
- package/src/storage/sqlShard/migrations.ts +66 -1
- package/src/storage/sqlShard/sqlShardStorage.ts +56 -55
- package/src/storage/sqlShard/tables.ts +6 -6
- package/src/types.ts +1 -2
- package/dist/esm/ClientConnection.d.ts +0 -19
- package/dist/esm/ClientConnection.js.map +0 -1
- package/dist/esm/MessageSender.d.ts +0 -5
- package/dist/esm/MessageSender.js.map +0 -1
- package/dist/esm/Presence.d.ts +0 -18
- package/dist/esm/Presence.js +0 -65
- package/dist/esm/Presence.js.map +0 -1
- package/dist/esm/ReplicaKeepaliveTimers.d.ts +0 -17
- package/dist/esm/ReplicaKeepaliveTimers.js +0 -27
- package/dist/esm/ReplicaKeepaliveTimers.js.map +0 -1
- package/dist/esm/Server.d.ts +0 -166
- package/dist/esm/Server.js +0 -572
- package/dist/esm/Server.js.map +0 -1
- package/dist/esm/ServerLibrary.d.ts +0 -64
- package/dist/esm/ServerLibrary.js.map +0 -1
- package/dist/esm/ServerLibrary.test.js.map +0 -1
- package/dist/esm/storage/sql/SqlBaselines.d.ts +0 -22
- package/dist/esm/storage/sql/SqlBaselines.js +0 -107
- package/dist/esm/storage/sql/SqlBaselines.js.map +0 -1
- package/dist/esm/storage/sql/SqlFileMetadata.d.ts +0 -18
- package/dist/esm/storage/sql/SqlFileMetadata.js +0 -69
- package/dist/esm/storage/sql/SqlFileMetadata.js.map +0 -1
- package/dist/esm/storage/sql/SqlOperations.d.ts +0 -19
- package/dist/esm/storage/sql/SqlOperations.js +0 -122
- package/dist/esm/storage/sql/SqlOperations.js.map +0 -1
- package/dist/esm/storage/sql/SqlReplicas.d.ts +0 -32
- package/dist/esm/storage/sql/SqlReplicas.js +0 -163
- package/dist/esm/storage/sql/SqlReplicas.js.map +0 -1
- package/dist/esm/storage/sql/database.d.ts +0 -9
- package/dist/esm/storage/sql/database.js +0 -24
- package/dist/esm/storage/sql/database.js.map +0 -1
- package/dist/esm/storage/sql/migrations/v1.d.ts +0 -3
- package/dist/esm/storage/sql/migrations/v1.js +0 -59
- package/dist/esm/storage/sql/migrations/v1.js.map +0 -1
- package/dist/esm/storage/sql/migrations/v2.d.ts +0 -3
- package/dist/esm/storage/sql/migrations/v2.js +0 -16
- package/dist/esm/storage/sql/migrations/v2.js.map +0 -1
- package/dist/esm/storage/sql/migrations.d.ts +0 -7
- package/dist/esm/storage/sql/migrations.js +0 -4
- package/dist/esm/storage/sql/migrations.js.map +0 -1
- package/dist/esm/storage/sql/sqlStorage.d.ts +0 -6
- package/dist/esm/storage/sql/sqlStorage.js +0 -35
- package/dist/esm/storage/sql/sqlStorage.js.map +0 -1
- package/dist/esm/storage/sql/tables.d.ts +0 -44
- package/dist/esm/storage/sql/tables.js +0 -2
- package/dist/esm/storage/sql/tables.js.map +0 -1
- package/dist/esm/storage/sqlShard/transfer.d.ts +0 -7
- package/dist/esm/storage/sqlShard/transfer.js +0 -90
- package/dist/esm/storage/sqlShard/transfer.js.map +0 -1
- package/dist/esm/storage/sqlShard/transfer.test.d.ts +0 -1
- package/dist/esm/storage/sqlShard/transfer.test.js +0 -109
- package/dist/esm/storage/sqlShard/transfer.test.js.map +0 -1
- package/src/MessageSender.ts +0 -10
- package/src/Presence.ts +0 -86
- package/src/ReplicaKeepaliveTimers.ts +0 -34
- package/src/Server.ts +0 -859
- package/src/storage/sql/SqlBaselines.ts +0 -147
- package/src/storage/sql/SqlFileMetadata.ts +0 -94
- package/src/storage/sql/SqlOperations.ts +0 -160
- package/src/storage/sql/SqlReplicas.ts +0 -234
- package/src/storage/sql/database.ts +0 -32
- package/src/storage/sql/migrations/v1.ts +0 -64
- package/src/storage/sql/migrations/v2.ts +0 -20
- package/src/storage/sql/migrations.ts +0 -4
- package/src/storage/sql/sqlStorage.ts +0 -57
- package/src/storage/sql/tables.ts +0 -51
- package/src/storage/sqlShard/transfer.test.ts +0 -149
- package/src/storage/sqlShard/transfer.ts +0 -123
- /package/dist/esm/{MessageSender.js → connections/MessageSender.js} +0 -0
- /package/dist/esm/{ServerLibrary.test.d.ts → libraries/Library.test.d.ts} +0 -0
package/bin/server.mjs
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { serve } from '@hono/node-server';
|
|
3
|
+
import { serveStatic } from '@hono/node-server/serve-static';
|
|
4
|
+
import { Hono } from 'hono';
|
|
5
|
+
import { cors } from 'hono/cors';
|
|
4
6
|
import minimist from 'minimist';
|
|
5
|
-
import { parse, URLSearchParams } from 'url';
|
|
6
7
|
import {
|
|
8
|
+
createHttpRouter,
|
|
9
|
+
createNodeWebsocketHandler,
|
|
10
|
+
} from '../dist/esm/adapters/node/index.js';
|
|
11
|
+
import {
|
|
12
|
+
createVerdant,
|
|
7
13
|
LocalFileStorage,
|
|
8
14
|
ReplicaType,
|
|
9
|
-
Server,
|
|
10
15
|
TokenProvider,
|
|
11
16
|
} from '../dist/esm/index.js';
|
|
12
|
-
import {
|
|
17
|
+
import { sqlShardStorage } from '../dist/esm/storage/index.js';
|
|
13
18
|
|
|
14
19
|
const argv = minimist(process.argv.slice(1));
|
|
15
20
|
|
|
@@ -17,119 +22,117 @@ const PORT = argv.port || argv.p || process.env.VERDANT_PORT || 3242;
|
|
|
17
22
|
const SECRET =
|
|
18
23
|
argv.secret || process.env.VERDANT_SECRET || 'notsecretnotsecretnotsecret';
|
|
19
24
|
|
|
20
|
-
const tokenProvider = new TokenProvider({
|
|
25
|
+
const tokenProvider = new TokenProvider({
|
|
26
|
+
secret: SECRET,
|
|
27
|
+
});
|
|
21
28
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
29
|
+
// the "core" is a service API for interacting with libraries.
|
|
30
|
+
// you could plug this into your own HTTP or other kind of interface,
|
|
31
|
+
// but further on are some Hono-based tools to help get started.
|
|
32
|
+
const core = createVerdant({
|
|
33
|
+
// SQLite databases for each library are stored in this directory
|
|
34
|
+
storage: sqlShardStorage({
|
|
35
|
+
databasesDirectory: 'verdant-dbs',
|
|
36
|
+
}),
|
|
37
|
+
// implement .get to retrieve detailed profile information for sync users
|
|
38
|
+
profiles: {
|
|
39
|
+
get: async (userId) => {
|
|
40
|
+
// you could fetch a profile record from a database here to augment
|
|
41
|
+
// this profile with name, image, etc.
|
|
42
|
+
// values will be cached, so don't worry too much about timing.
|
|
43
|
+
return { id: userId };
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
// required to validate sync access tokens generated from
|
|
47
|
+
// your API
|
|
48
|
+
tokenSecret: SECRET,
|
|
49
|
+
// optional; enables file sync
|
|
50
|
+
fileStorage: new LocalFileStorage({
|
|
51
|
+
rootDirectory: 'verdant-files',
|
|
52
|
+
// this matches an API you must host (see below)
|
|
53
|
+
host: `http://localhost:${PORT}/files`,
|
|
54
|
+
log: (level, ...args) => console.log(level, ...args),
|
|
55
|
+
}),
|
|
56
|
+
// optional logger
|
|
57
|
+
log: (level, ...args) => console.log(level, ...args),
|
|
58
|
+
});
|
|
39
59
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
console.log('Serving file', file);
|
|
44
|
-
const fileStream = createReadStream(`files/${file}`);
|
|
45
|
-
fileStream.pipe(res);
|
|
46
|
-
fileStream.on('error', (err) => {
|
|
47
|
-
res.writeHead(404);
|
|
48
|
-
res.end();
|
|
49
|
-
});
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
60
|
+
// our verdant router will be mounted to a subpath
|
|
61
|
+
// and handle all HTTP sync requests
|
|
62
|
+
const verdantRouter = createHttpRouter(core);
|
|
52
63
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
64
|
+
// this is "your" app -- Verdant lives in it,
|
|
65
|
+
// but you control the API.
|
|
66
|
+
const app = new Hono()
|
|
67
|
+
.use('*', async (c, next) => {
|
|
68
|
+
// simple request logger
|
|
69
|
+
console.log(`${c.req.method} ${c.req.url}`);
|
|
70
|
+
await next();
|
|
71
|
+
})
|
|
72
|
+
.use(
|
|
73
|
+
'*',
|
|
74
|
+
cors({
|
|
75
|
+
origin: (o) => o,
|
|
76
|
+
credentials: true,
|
|
77
|
+
allowHeaders: ['Content-Type', 'Authorization', 'X-Request-Id'],
|
|
78
|
+
}),
|
|
79
|
+
)
|
|
80
|
+
.route('/verdant', verdantRouter)
|
|
81
|
+
// issue sync tokens
|
|
82
|
+
.get('/auth/:libraryId', async (ctx) => {
|
|
83
|
+
// here you authenticate your user, authorize
|
|
84
|
+
// their access to a particular library, and
|
|
85
|
+
// issue a token.
|
|
86
|
+
const library = ctx.req.param('libraryId');
|
|
87
|
+
|
|
88
|
+
const user = ctx.req.query('userId');
|
|
89
|
+
const type = ctx.req.query('type') || ReplicaType.Realtime;
|
|
58
90
|
|
|
59
|
-
//
|
|
60
|
-
|
|
91
|
+
// remember to check that the user is allowed to sync
|
|
92
|
+
// to this library
|
|
61
93
|
|
|
62
94
|
const token = tokenProvider.getToken({
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
type
|
|
95
|
+
libraryId: library,
|
|
96
|
+
userId: user,
|
|
97
|
+
// this subpath matches the verdantRouter mount
|
|
98
|
+
syncEndpoint: `http://localhost:${PORT}/verdant`,
|
|
99
|
+
type,
|
|
68
100
|
});
|
|
69
|
-
|
|
70
|
-
|
|
101
|
+
return ctx.json({
|
|
102
|
+
accessToken: token,
|
|
71
103
|
});
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
res.writeHead(404);
|
|
81
|
-
res.end();
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
const verdant = new Server({
|
|
86
|
-
storage: sqlStorage({
|
|
87
|
-
databaseFile: 'verdant.sqlite',
|
|
88
|
-
}),
|
|
89
|
-
tokenSecret: SECRET,
|
|
90
|
-
fileStorage: new LocalFileStorage({
|
|
91
|
-
rootDirectory: 'files',
|
|
92
|
-
host: `http://localhost:${3242}/files`,
|
|
93
|
-
}),
|
|
94
|
-
profiles: {
|
|
95
|
-
get: (userId) => ({
|
|
96
|
-
id: userId,
|
|
104
|
+
})
|
|
105
|
+
// (optional) serve uploaded files
|
|
106
|
+
// if you use LocalFileStorage
|
|
107
|
+
.get(
|
|
108
|
+
'/files/*',
|
|
109
|
+
serveStatic({
|
|
110
|
+
root: 'verdant-files',
|
|
111
|
+
rewriteRequestPath: (path) => path.replace('/files/', ''),
|
|
97
112
|
}),
|
|
98
|
-
|
|
99
|
-
httpServer: http,
|
|
100
|
-
log: (level, ...rest) => {
|
|
101
|
-
if (process.env.VERDANT_LOG === 'false') {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
if (level === 'debug' && process.env.VERDANT_DEBUG !== 'true') {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
console.log(level, ...rest);
|
|
108
|
-
},
|
|
109
|
-
});
|
|
113
|
+
);
|
|
110
114
|
|
|
111
|
-
|
|
112
|
-
|
|
115
|
+
const server = serve({
|
|
116
|
+
fetch: app.fetch,
|
|
117
|
+
port: parseInt(PORT.toString(), 10),
|
|
113
118
|
});
|
|
114
119
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
console.log(
|
|
120
|
-
`It's not recommended you use this server in production. It's a convenient way to test syncing locally, but has no authorization.`,
|
|
121
|
-
);
|
|
122
|
-
console.log(``);
|
|
123
|
-
console.log(
|
|
124
|
-
`To connect your client, supply "http://localhost:${PORT}/auth/<libraryId>?userId=<userId>" to sync.authEndpoint.`,
|
|
125
|
-
);
|
|
126
|
-
console.log(
|
|
127
|
-
`<libraryId> and <userId> are up to you and your app. Clients on the same library ID will sync data.`,
|
|
128
|
-
);
|
|
120
|
+
// handle websocket sync! must be done like this, not included
|
|
121
|
+
// in createHttpRouter!
|
|
122
|
+
const onUpgrade = createNodeWebsocketHandler(core);
|
|
123
|
+
server.addListener('upgrade', onUpgrade);
|
|
129
124
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
125
|
+
server.addListener('listening', () => {
|
|
126
|
+
console.log(`🌿 Verdant standalone server listening on port ${PORT}`);
|
|
127
|
+
console.log(``);
|
|
128
|
+
console.log(
|
|
129
|
+
`Never use this server in production. It's a convenient way to test syncing locally, but has no authorization.`,
|
|
130
|
+
);
|
|
131
|
+
console.log(``);
|
|
132
|
+
console.log(
|
|
133
|
+
`To connect your client, import \`cliSync\` and pass \`cliSync(<libraryId>)\` to \`sync\`, or supply "http://localhost:${PORT}/auth/<libraryId>?userId=<userId>" to sync.authEndpoint.`,
|
|
134
|
+
);
|
|
135
|
+
console.log(
|
|
136
|
+
`<libraryId> and <userId> are up to you and your app. Clients on the same library ID will sync data.`,
|
|
137
|
+
);
|
|
135
138
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenVerifier.js","sourceRoot":"","sources":["../../src/TokenVerifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAe,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,GAAmB,MAAM,cAAc,CAAC;AAE/C,MAAM,OAAO,aAAa;IACzB,YAAoB,MAA0B;QAA1B,WAAM,GAAN,MAAM,CAAoB;
|
|
1
|
+
{"version":3,"file":"TokenVerifier.js","sourceRoot":"","sources":["../../src/TokenVerifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAe,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,GAAmB,MAAM,cAAc,CAAC;AAE/C,MAAM,OAAO,aAAa;IACzB,YAAoB,MAA0B;QAA1B,WAAM,GAAN,MAAM,CAAoB;QAM9C,gBAAW,GAAG,CAAC,KAAa,EAAa,EAAE;YAC1C,IAAI,CAAC;gBACJ,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;oBACrD,QAAQ,EAAE,KAAK;iBACf,CAAe,CAAC;gBACjB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBACpB,OAAO;oBACN,MAAM,EAAE,OAAO,CAAC,GAAG;oBACnB,SAAS,EAAE,OAAO,CAAC,GAAG;oBACtB,YAAY,EAAE,OAAO,CAAC,GAAG;oBACzB,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,KAAK;iBACL,CAAC;YACH,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACZ,IAAI,CAAC,YAAY,GAAG,CAAC,iBAAiB,EAAE,CAAC;oBACxC,MAAM,IAAI,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBACxD,CAAC;gBACD,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC;oBACxB,MAAM,IAAI,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;gBACzD,CAAC;gBACD,MAAM,IAAI,YAAY,CACrB,YAAY,CAAC,IAAI,CAAC,UAAU,EAC5B,SAAS,EACT,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CACjB,CAAC;YACH,CAAC;QACF,CAAC,CAAC;IAjC+C,CAAC;IAElD,IAAI,MAAM;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAC3B,CAAC;CA8BD"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { VerdantError } from '@verdant-web/common';
|
|
2
|
+
export const errorHandler = (err, ctx) => {
|
|
3
|
+
if (err instanceof VerdantError) {
|
|
4
|
+
if (err.code > VerdantError.Code.Unexpected) {
|
|
5
|
+
console.error('Verdant error:', err);
|
|
6
|
+
}
|
|
7
|
+
return ctx.json(err.toResponse(), err.httpStatus);
|
|
8
|
+
}
|
|
9
|
+
console.error('Unexpected error:', err);
|
|
10
|
+
return ctx.json(new VerdantError(VerdantError.Code.Unexpected).toResponse(), 500);
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=errorHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../../../src/adapters/node/errorHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAInD,MAAM,CAAC,MAAM,YAAY,GAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IACtD,IAAI,GAAG,YAAY,YAAY,EAAE,CAAC;QACjC,IAAI,GAAG,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YAC7C,OAAO,CAAC,KAAK,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,GAAG,CAAC,UAAkC,CAAC,CAAC;IAC3E,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAC;IACxC,OAAO,GAAG,CAAC,IAAI,CACd,IAAI,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,UAAU,EAAE,EAC3D,GAAG,CACH,CAAC;AACH,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getRequestToken(req: Request): string | null;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { VerdantError } from '@verdant-web/common';
|
|
2
|
+
export function getRequestToken(req) {
|
|
3
|
+
if (req.method === 'OPTIONS') {
|
|
4
|
+
// preflight request, np
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
// websocket compat - we only receive one header...
|
|
8
|
+
const protocolHeader = req.headers.get('Sec-WebSocket-Protocol');
|
|
9
|
+
if (protocolHeader) {
|
|
10
|
+
const [type, token] = protocolHeader.split(',');
|
|
11
|
+
if (type === 'Bearer') {
|
|
12
|
+
return token.trim();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const authHeader = req.headers.get('Authorization');
|
|
16
|
+
if (!authHeader) {
|
|
17
|
+
throw new VerdantError(VerdantError.Code.NoToken, undefined, 'Authorization header is required');
|
|
18
|
+
}
|
|
19
|
+
const [type, token] = authHeader.split(' ');
|
|
20
|
+
if (type !== 'Bearer') {
|
|
21
|
+
throw new VerdantError(VerdantError.Code.InvalidToken, undefined, 'Authorization type must be Bearer');
|
|
22
|
+
}
|
|
23
|
+
return token;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=getRequestToken.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getRequestToken.js","sourceRoot":"","sources":["../../../../src/adapters/node/getRequestToken.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,MAAM,UAAU,eAAe,CAAC,GAAY;IAC3C,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC9B,wBAAwB;QACxB,OAAO,IAAI,CAAC;IACb,CAAC;IACD,mDAAmD;IACnD,MAAM,cAAc,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACjE,IAAI,cAAc,EAAE,CAAC;QACpB,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAChD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;QACrB,CAAC;IACF,CAAC;IAED,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACpD,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,MAAM,IAAI,YAAY,CACrB,YAAY,CAAC,IAAI,CAAC,OAAO,EACzB,SAAS,EACT,kCAAkC,CAClC,CAAC;IACH,CAAC;IACD,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACvB,MAAM,IAAI,YAAY,CACrB,YAAY,CAAC,IAAI,CAAC,YAAY,EAC9B,SAAS,EACT,mCAAmC,CACnC,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { SingleNodeLibraryManager } from '../../libraries/singleNode.js';
|
|
2
|
+
export declare function createHttpRouter(core: SingleNodeLibraryManager): import("hono/hono-base").HonoBase<{}, {
|
|
3
|
+
"*": {};
|
|
4
|
+
} & {
|
|
5
|
+
"/": {
|
|
6
|
+
$post: {
|
|
7
|
+
input: {};
|
|
8
|
+
output: {};
|
|
9
|
+
outputFormat: string;
|
|
10
|
+
status: import("hono/utils/http-status").StatusCode;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
} & {
|
|
14
|
+
"/files/:fileId": {
|
|
15
|
+
$post: {
|
|
16
|
+
input: {
|
|
17
|
+
param: {
|
|
18
|
+
fileId: string;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
output: {
|
|
22
|
+
success: true;
|
|
23
|
+
};
|
|
24
|
+
outputFormat: "json";
|
|
25
|
+
status: 200;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
} & {
|
|
29
|
+
"/files/:fileId": {
|
|
30
|
+
$get: {
|
|
31
|
+
input: {
|
|
32
|
+
param: {
|
|
33
|
+
fileId: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
output: {
|
|
37
|
+
libraryId: string;
|
|
38
|
+
id: string;
|
|
39
|
+
name: string;
|
|
40
|
+
type: string;
|
|
41
|
+
url: string | null;
|
|
42
|
+
};
|
|
43
|
+
outputFormat: "json";
|
|
44
|
+
status: 200;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
}, "/">;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { VerdantError } from '@verdant-web/common';
|
|
2
|
+
import { Hono } from 'hono';
|
|
3
|
+
import { Readable } from 'stream';
|
|
4
|
+
import { errorHandler } from './errorHandler.js';
|
|
5
|
+
import { tokenMiddleware } from './tokenMiddleware.js';
|
|
6
|
+
export function createHttpRouter(core) {
|
|
7
|
+
const mw = tokenMiddleware(core.tokenVerifier);
|
|
8
|
+
const app = new Hono()
|
|
9
|
+
.onError(errorHandler)
|
|
10
|
+
.use('*', async (c, next) => {
|
|
11
|
+
core.log('debug', 'Incoming request', {
|
|
12
|
+
method: c.req.method,
|
|
13
|
+
path: c.req.path,
|
|
14
|
+
});
|
|
15
|
+
await next();
|
|
16
|
+
})
|
|
17
|
+
.post('/', mw, async (ctx) => {
|
|
18
|
+
var _a;
|
|
19
|
+
const key = ctx.get('key');
|
|
20
|
+
const info = ctx.get('tokenInfo');
|
|
21
|
+
const microserver = await core.get(info.libraryId);
|
|
22
|
+
const finish = (_a = microserver.onRequest) === null || _a === void 0 ? void 0 : _a.call(microserver, key, ctx.req.raw, info);
|
|
23
|
+
const body = (await ctx.req.json());
|
|
24
|
+
if (!(body === null || body === void 0 ? void 0 : body.messages)) {
|
|
25
|
+
throw new VerdantError(VerdantError.Code.BodyRequired, undefined, 'Invalid request body');
|
|
26
|
+
}
|
|
27
|
+
for (const message of body.messages) {
|
|
28
|
+
await microserver.handleMessage(key, info, message);
|
|
29
|
+
}
|
|
30
|
+
const res = finish();
|
|
31
|
+
return res;
|
|
32
|
+
})
|
|
33
|
+
.post('/files/:fileId', mw, async (ctx) => {
|
|
34
|
+
const info = ctx.get('tokenInfo');
|
|
35
|
+
const id = ctx.req.param('fileId');
|
|
36
|
+
if (!id) {
|
|
37
|
+
throw new VerdantError(VerdantError.Code.NotFound, undefined, 'Supply a file ID as a path parameters');
|
|
38
|
+
}
|
|
39
|
+
// TODO: remove this once I figure out why request file body parsing is
|
|
40
|
+
// hanging indefinitely.
|
|
41
|
+
if (core.__testMode) {
|
|
42
|
+
const microserver = await core.get(info.libraryId);
|
|
43
|
+
core.log('warn', 'TEST MODE: FILE WILL NOT BE WRITTEN', '\n', 'This Verdant server was launched in Test Mode. This is only meant for use', 'in automated testing environments. The file just uploaded WILL NOT be', 'stored on disk or retrievable.');
|
|
44
|
+
// Fake a file upload for testing
|
|
45
|
+
await microserver.uploadFile(new Readable({
|
|
46
|
+
read() {
|
|
47
|
+
this.push('test');
|
|
48
|
+
this.push(null);
|
|
49
|
+
},
|
|
50
|
+
}), {
|
|
51
|
+
id,
|
|
52
|
+
libraryId: info.libraryId,
|
|
53
|
+
fileName: 'test.txt',
|
|
54
|
+
type: 'text/plain',
|
|
55
|
+
});
|
|
56
|
+
return ctx.json({ success: true }, 200);
|
|
57
|
+
}
|
|
58
|
+
const form = await ctx.req.parseBody();
|
|
59
|
+
const file = form.file;
|
|
60
|
+
if (!file || !(file instanceof File)) {
|
|
61
|
+
throw new VerdantError(VerdantError.Code.InvalidRequest, undefined, 'No file was uploaded to the `file` form key');
|
|
62
|
+
}
|
|
63
|
+
const microserver = await core.get(info.libraryId);
|
|
64
|
+
const fileStream = file.stream();
|
|
65
|
+
await microserver.uploadFile(fileStream, {
|
|
66
|
+
id,
|
|
67
|
+
libraryId: info.libraryId,
|
|
68
|
+
fileName: file.name,
|
|
69
|
+
type: file.type,
|
|
70
|
+
});
|
|
71
|
+
return ctx.json({ success: true }, 200);
|
|
72
|
+
})
|
|
73
|
+
.get('/files/:fileId', mw, async (ctx) => {
|
|
74
|
+
const info = ctx.get('tokenInfo');
|
|
75
|
+
const id = ctx.req.param('fileId');
|
|
76
|
+
if (!id) {
|
|
77
|
+
throw new VerdantError(VerdantError.Code.NotFound, undefined, 'Supply a file ID as a path parameters');
|
|
78
|
+
}
|
|
79
|
+
const microserver = await core.get(info.libraryId);
|
|
80
|
+
const file = await microserver.getFileInfo(id);
|
|
81
|
+
if (!file) {
|
|
82
|
+
throw new VerdantError(VerdantError.Code.NotFound, undefined, `File with ID ${id} not found`);
|
|
83
|
+
}
|
|
84
|
+
return ctx.json(file, 200);
|
|
85
|
+
});
|
|
86
|
+
return app;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=httpRouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"httpRouter.js","sourceRoot":"","sources":["../../../../src/adapters/node/httpRouter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,MAAM,UAAU,gBAAgB,CAAC,IAA8B;IAC9D,MAAM,EAAE,GAAG,eAAe,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC/C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAO;SACzB,OAAO,CAAC,YAAY,CAAC;SACrB,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QAC3B,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,kBAAkB,EAAE;YACrC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM;YACpB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI;SAChB,CAAC,CAAC;QACH,MAAM,IAAI,EAAE,CAAC;IACd,CAAC,CAAC;SACD,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;;QAC5B,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3B,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAElC,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAEnD,MAAM,MAAM,GAAG,MAAA,WAAW,CAAC,SAAS,4DAAG,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAE/D,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAGtB,CAAC;QAEb,IAAI,CAAC,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,QAAQ,CAAA,EAAE,CAAC;YACrB,MAAM,IAAI,YAAY,CACrB,YAAY,CAAC,IAAI,CAAC,YAAY,EAC9B,SAAS,EACT,sBAAsB,CACtB,CAAC;QACH,CAAC;QAED,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACrC,MAAM,WAAW,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;QAErB,OAAO,GAAG,CAAC;IACZ,CAAC,CAAC;SACD,IAAI,CAAC,gBAAgB,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACzC,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAElC,MAAM,EAAE,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,CAAC,EAAE,EAAE,CAAC;YACT,MAAM,IAAI,YAAY,CACrB,YAAY,CAAC,IAAI,CAAC,QAAQ,EAC1B,SAAS,EACT,uCAAuC,CACvC,CAAC;QACH,CAAC;QAED,uEAAuE;QACvE,wBAAwB;QACxB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAEnD,IAAI,CAAC,GAAG,CACP,MAAM,EACN,qCAAqC,EACrC,IAAI,EACJ,2EAA2E,EAC3E,uEAAuE,EACvE,gCAAgC,CAChC,CAAC;YACF,iCAAiC;YACjC,MAAM,WAAW,CAAC,UAAU,CAC3B,IAAI,QAAQ,CAAC;gBACZ,IAAI;oBACH,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBAClB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACjB,CAAC;aACD,CAAC,EACF;gBACC,EAAE;gBACF,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,QAAQ,EAAE,UAAU;gBACpB,IAAI,EAAE,YAAY;aAClB,CACD,CAAC;YACF,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;QACzC,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,YAAY,CACrB,YAAY,CAAC,IAAI,CAAC,cAAc,EAChC,SAAS,EACT,6CAA6C,CAC7C,CAAC;QACH,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAEnD,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACjC,MAAM,WAAW,CAAC,UAAU,CAAC,UAAU,EAAE;YACxC,EAAE;YACF,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,QAAQ,EAAE,IAAI,CAAC,IAAI;YACnB,IAAI,EAAE,IAAI,CAAC,IAAI;SACf,CAAC,CAAC;QAEH,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;IACzC,CAAC,CAAC;SACD,GAAG,CAAC,gBAAgB,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACxC,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAElC,MAAM,EAAE,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,CAAC,EAAE,EAAE,CAAC;YACT,MAAM,IAAI,YAAY,CACrB,YAAY,CAAC,IAAI,CAAC,QAAQ,EAC1B,SAAS,EACT,uCAAuC,CACvC,CAAC;QACH,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAEnD,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAC/C,IAAI,CAAC,IAAI,EAAE,CAAC;YACX,MAAM,IAAI,YAAY,CACrB,YAAY,CAAC,IAAI,CAAC,QAAQ,EAC1B,SAAS,EACT,gBAAgB,EAAE,YAAY,CAC9B,CAAC;QACH,CAAC;QAED,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IACJ,OAAO,GAAG,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { errorHandler } from '../node/errorHandler.js';
|
|
2
|
+
export { getRequestToken } from '../node/getRequestToken.js';
|
|
3
|
+
export * from './httpRouter.js';
|
|
4
|
+
export { tokenMiddleware } from './tokenMiddleware.js';
|
|
5
|
+
export * from './websocketHandler.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/adapters/node/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,cAAc,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { generateId } from '@verdant-web/common';
|
|
2
|
+
import { createMiddleware } from 'hono/factory';
|
|
3
|
+
import { getRequestToken } from '../node/getRequestToken.js';
|
|
4
|
+
export const tokenMiddleware = (tokenVerifier) => createMiddleware((ctx, next) => {
|
|
5
|
+
if (ctx.req.method === 'OPTIONS') {
|
|
6
|
+
// preflight request, np
|
|
7
|
+
return next();
|
|
8
|
+
}
|
|
9
|
+
const requestToken = getRequestToken(ctx.req.raw);
|
|
10
|
+
const key = generateId();
|
|
11
|
+
const info = tokenVerifier.verifyToken(requestToken);
|
|
12
|
+
ctx.set('key', key);
|
|
13
|
+
ctx.set('tokenInfo', info);
|
|
14
|
+
// if a Sec-Websocket-Protocol header was sent, we need to echo it back
|
|
15
|
+
// so the websocket connection is accepted by the client
|
|
16
|
+
const protocolHeader = ctx.req.header('Sec-WebSocket-Protocol');
|
|
17
|
+
if (protocolHeader) {
|
|
18
|
+
ctx.res.headers.set('Sec-WebSocket-Protocol', requestToken);
|
|
19
|
+
}
|
|
20
|
+
return next();
|
|
21
|
+
});
|
|
22
|
+
//# sourceMappingURL=tokenMiddleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenMiddleware.js","sourceRoot":"","sources":["../../../../src/adapters/node/tokenMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,aAA4B,EAAE,EAAE,CAC/D,gBAAgB,CAKb,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;IAChB,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAClC,wBAAwB;QACxB,OAAO,IAAI,EAAE,CAAC;IACf,CAAC;IACD,MAAM,YAAY,GAAG,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;IAEnD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,aAAa,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;IACrD,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACpB,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IAE3B,uEAAuE;IACvE,wDAAwD;IACxD,MAAM,cAAc,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC;IAChE,IAAI,cAAc,EAAE,CAAC;QACpB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,YAAY,CAAC,CAAC;IAC7D,CAAC;IAED,OAAO,IAAI,EAAE,CAAC;AACf,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { IncomingMessage } from 'node:http';
|
|
2
|
+
import internal from 'node:stream';
|
|
3
|
+
import { SingleNodeLibraryManager } from '../../libraries/singleNode.js';
|
|
4
|
+
export declare function createNodeWebsocketHandler(core: SingleNodeLibraryManager): (req: IncomingMessage, socket: internal.Duplex, head: Buffer) => void;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { VerdantError, assert, generateId, } from '@verdant-web/common';
|
|
2
|
+
import { URL } from 'url';
|
|
3
|
+
import { WebSocketServer } from 'ws';
|
|
4
|
+
export function createNodeWebsocketHandler(core) {
|
|
5
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
6
|
+
wss.on('connection', async (ws, req, info) => {
|
|
7
|
+
const key = generateId();
|
|
8
|
+
const microserver = await core.get(info.libraryId);
|
|
9
|
+
microserver.clientConnections.addSocket(key, ws, info);
|
|
10
|
+
ws.on('message', async (message) => {
|
|
11
|
+
const data = JSON.parse(message.toString());
|
|
12
|
+
await microserver.handleMessage(key, info, data);
|
|
13
|
+
});
|
|
14
|
+
ws.on('close', () => {
|
|
15
|
+
microserver.clientConnections.remove(key);
|
|
16
|
+
core.log('debug', 'Connection closed', { key, userId: info.userId });
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
function getRequestToken(req) {
|
|
20
|
+
if (req.headers['sec-websocket-protocol']) {
|
|
21
|
+
// hack: read the token from the websocket protocol header
|
|
22
|
+
const [type, token] = req.headers['sec-websocket-protocol'].split(',');
|
|
23
|
+
if (type === 'Bearer') {
|
|
24
|
+
return token.trim();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
assert(req.url, 'Request URL is required');
|
|
28
|
+
const url = new URL(req.url, 'http://localhost');
|
|
29
|
+
const token = url.searchParams.get('token');
|
|
30
|
+
if (!token) {
|
|
31
|
+
throw new VerdantError(VerdantError.Code.NoToken, undefined, 'Token query parameter is required');
|
|
32
|
+
}
|
|
33
|
+
return token;
|
|
34
|
+
}
|
|
35
|
+
return (req, socket, head) => {
|
|
36
|
+
try {
|
|
37
|
+
const token = getRequestToken(req);
|
|
38
|
+
const info = core.tokenVerifier.verifyToken(token);
|
|
39
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
40
|
+
wss.emit('connection', ws, req, info);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
if (err instanceof VerdantError && err.httpStatus === 401) {
|
|
45
|
+
socket.write('HTTP/1.1 401 Unauthorized\r\n' +
|
|
46
|
+
'Connection: close\r\n' +
|
|
47
|
+
'Content-Length: 0\r\n' +
|
|
48
|
+
'\r\n');
|
|
49
|
+
}
|
|
50
|
+
socket.destroy();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=websocketHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocketHandler.js","sourceRoot":"","sources":["../../../../src/adapters/node/websocketHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ServerMessage } from '@verdant-web/common';
|
|
2
|
+
import { IncomingMessage, ServerResponse } from 'http';
|
|
3
|
+
import { WebSocket as WSWebSocket } from 'ws';
|
|
4
|
+
import { UserProfileLoader } from '../Profiles.js';
|
|
5
|
+
import { TokenInfo } from '../TokenVerifier.js';
|
|
6
|
+
import { MessageSender } from './MessageSender.js';
|
|
7
|
+
import { Presence } from './Presence.js';
|
|
8
|
+
export declare class ClientConnectionManager implements MessageSender {
|
|
9
|
+
private readonly connections;
|
|
10
|
+
readonly presence: Presence;
|
|
11
|
+
constructor({ profiles }: {
|
|
12
|
+
profiles: UserProfileLoader<any>;
|
|
13
|
+
});
|
|
14
|
+
addSocket: (key: string, socket: WebSocket | WSWebSocket, info: TokenInfo) => void;
|
|
15
|
+
addRequest: (key: string, request: IncomingMessage, response: ServerResponse, info: TokenInfo) => () => void;
|
|
16
|
+
addFetch: (key: string, req: Request, info: TokenInfo) => (() => Response);
|
|
17
|
+
remove: (key: string) => void;
|
|
18
|
+
respond: (key: string, message: ServerMessage) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Broadcasts a message to all live-connected clients in a library.
|
|
21
|
+
* This does not reach push/pull clients.
|
|
22
|
+
*/
|
|
23
|
+
broadcast: (message: ServerMessage, omitKeys?: string[]) => void;
|
|
24
|
+
disconnectAll: () => void;
|
|
25
|
+
}
|