@enbox/dwn-server 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/dist/esm/src/config.d.ts +2 -6
- package/dist/esm/src/config.d.ts.map +1 -1
- package/dist/esm/src/config.js +4 -8
- package/dist/esm/src/config.js.map +1 -1
- package/dist/esm/src/connection/connection-manager.d.ts +9 -9
- package/dist/esm/src/connection/connection-manager.d.ts.map +1 -1
- package/dist/esm/src/connection/connection-manager.js +5 -3
- package/dist/esm/src/connection/connection-manager.js.map +1 -1
- package/dist/esm/src/connection/socket-connection.d.ts +18 -17
- package/dist/esm/src/connection/socket-connection.d.ts.map +1 -1
- package/dist/esm/src/connection/socket-connection.js +26 -35
- package/dist/esm/src/connection/socket-connection.js.map +1 -1
- package/dist/esm/src/dwn-error.js.map +1 -1
- package/dist/esm/src/dwn-server.d.ts +5 -6
- package/dist/esm/src/dwn-server.d.ts.map +1 -1
- package/dist/esm/src/dwn-server.js +3 -14
- package/dist/esm/src/dwn-server.js.map +1 -1
- package/dist/esm/src/http-api.d.ts +9 -11
- package/dist/esm/src/http-api.d.ts.map +1 -1
- package/dist/esm/src/http-api.js +450 -375
- package/dist/esm/src/http-api.js.map +1 -1
- package/dist/esm/src/json-rpc-handlers/dwn/process-message.d.ts.map +1 -1
- package/dist/esm/src/json-rpc-handlers/dwn/process-message.js +3 -3
- package/dist/esm/src/json-rpc-handlers/dwn/process-message.js.map +1 -1
- package/dist/esm/src/json-rpc-handlers/subscription/close.d.ts.map +1 -1
- package/dist/esm/src/json-rpc-handlers/subscription/close.js.map +1 -1
- package/dist/esm/src/json-rpc-socket.d.ts +1 -1
- package/dist/esm/src/json-rpc-socket.d.ts.map +1 -1
- package/dist/esm/src/json-rpc-socket.js +1 -1
- package/dist/esm/src/json-rpc-socket.js.map +1 -1
- package/dist/esm/src/lib/json-rpc-router.d.ts +3 -4
- package/dist/esm/src/lib/json-rpc-router.d.ts.map +1 -1
- package/dist/esm/src/lib/json-rpc-router.js.map +1 -1
- package/dist/esm/src/lib/json-rpc.d.ts.map +1 -1
- package/dist/esm/src/lib/json-rpc.js.map +1 -1
- package/dist/esm/src/main.js +0 -0
- package/dist/esm/src/metrics.d.ts +1 -1
- package/dist/esm/src/metrics.js.map +1 -1
- package/dist/esm/src/registration/proof-of-work-manager.d.ts +1 -1
- package/dist/esm/src/registration/proof-of-work-manager.d.ts.map +1 -1
- package/dist/esm/src/registration/proof-of-work-manager.js +3 -3
- package/dist/esm/src/registration/proof-of-work-manager.js.map +1 -1
- package/dist/esm/src/registration/registration-manager.d.ts +3 -3
- package/dist/esm/src/registration/registration-manager.d.ts.map +1 -1
- package/dist/esm/src/registration/registration-manager.js +6 -6
- package/dist/esm/src/registration/registration-manager.js.map +1 -1
- package/dist/esm/src/registration/registration-store.d.ts +1 -1
- package/dist/esm/src/registration/registration-store.d.ts.map +1 -1
- package/dist/esm/src/registration/registration-store.js.map +1 -1
- package/dist/esm/src/storage.d.ts +2 -2
- package/dist/esm/src/storage.d.ts.map +1 -1
- package/dist/esm/src/storage.js +5 -4
- package/dist/esm/src/storage.js.map +1 -1
- package/dist/esm/src/web5-connect/sql-ttl-cache.d.ts.map +1 -1
- package/dist/esm/src/web5-connect/sql-ttl-cache.js +3 -2
- package/dist/esm/src/web5-connect/sql-ttl-cache.js.map +1 -1
- package/dist/esm/src/web5-connect/web5-connect-server.d.ts.map +1 -1
- package/dist/esm/src/web5-connect/web5-connect-server.js +2 -2
- package/dist/esm/src/web5-connect/web5-connect-server.js.map +1 -1
- package/dist/esm/src/ws-api.d.ts +2 -4
- package/dist/esm/src/ws-api.d.ts.map +1 -1
- package/dist/esm/src/ws-api.js +6 -17
- package/dist/esm/src/ws-api.js.map +1 -1
- package/dist/esm/tests/common-scenario-validator.d.ts.map +1 -1
- package/dist/esm/tests/common-scenario-validator.js +2 -3
- package/dist/esm/tests/common-scenario-validator.js.map +1 -1
- package/dist/esm/tests/connection/connection-manager.spec.js +11 -9
- package/dist/esm/tests/connection/connection-manager.spec.js.map +1 -1
- package/dist/esm/tests/connection/socket-connection.spec.js +40 -18
- package/dist/esm/tests/connection/socket-connection.spec.js.map +1 -1
- package/dist/esm/tests/cors/http-api.browser.js +1 -1
- package/dist/esm/tests/cors/http-api.browser.js.map +1 -1
- package/dist/esm/tests/dwn-process-message.spec.js +4 -4
- package/dist/esm/tests/dwn-process-message.spec.js.map +1 -1
- package/dist/esm/tests/dwn-server.spec.js +8 -9
- package/dist/esm/tests/dwn-server.spec.js.map +1 -1
- package/dist/esm/tests/http-api.spec.js +92 -85
- package/dist/esm/tests/http-api.spec.js.map +1 -1
- package/dist/esm/tests/json-rpc-socket.spec.js +11 -9
- package/dist/esm/tests/json-rpc-socket.spec.js.map +1 -1
- package/dist/esm/tests/plugins/data-store-sqlite.d.ts +2 -2
- package/dist/esm/tests/plugins/data-store-sqlite.js +2 -2
- package/dist/esm/tests/plugins/event-log-sqlite.d.ts +2 -2
- package/dist/esm/tests/plugins/event-log-sqlite.js +2 -2
- package/dist/esm/tests/plugins/event-stream-in-memory.d.ts +2 -2
- package/dist/esm/tests/plugins/event-stream-in-memory.d.ts.map +1 -1
- package/dist/esm/tests/plugins/event-stream-in-memory.js +1 -1
- package/dist/esm/tests/plugins/event-stream-in-memory.js.map +1 -1
- package/dist/esm/tests/plugins/message-store-sqlite.d.ts +2 -2
- package/dist/esm/tests/plugins/message-store-sqlite.d.ts.map +1 -1
- package/dist/esm/tests/plugins/message-store-sqlite.js +2 -2
- package/dist/esm/tests/plugins/message-store-sqlite.js.map +1 -1
- package/dist/esm/tests/plugins/resumable-task-store-sqlite.d.ts +2 -2
- package/dist/esm/tests/plugins/resumable-task-store-sqlite.d.ts.map +1 -1
- package/dist/esm/tests/plugins/resumable-task-store-sqlite.js +2 -2
- package/dist/esm/tests/plugins/resumable-task-store-sqlite.js.map +1 -1
- package/dist/esm/tests/process-handler.spec.js +6 -6
- package/dist/esm/tests/process-handler.spec.js.map +1 -1
- package/dist/esm/tests/registration/proof-of-work-manager.spec.js +3 -4
- package/dist/esm/tests/registration/proof-of-work-manager.spec.js.map +1 -1
- package/dist/esm/tests/rpc-subscribe-close.spec.js +1 -1
- package/dist/esm/tests/rpc-subscribe-close.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.js +11 -10
- package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/registration.spec.js +16 -12
- package/dist/esm/tests/scenarios/registration.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/web5-connect.spec.js +12 -8
- package/dist/esm/tests/scenarios/web5-connect.spec.js.map +1 -1
- package/dist/esm/tests/test-dwn.d.ts.map +1 -1
- package/dist/esm/tests/test-dwn.js +9 -15
- package/dist/esm/tests/test-dwn.js.map +1 -1
- package/dist/esm/tests/utils.d.ts +3 -6
- package/dist/esm/tests/utils.d.ts.map +1 -1
- package/dist/esm/tests/utils.js +9 -18
- package/dist/esm/tests/utils.js.map +1 -1
- package/dist/esm/tests/ws-api.spec.js +28 -23
- package/dist/esm/tests/ws-api.spec.js.map +1 -1
- package/package.json +25 -44
- package/src/config.ts +15 -19
- package/src/connection/connection-manager.ts +18 -12
- package/src/connection/socket-connection.ts +52 -57
- package/src/dwn-error.ts +2 -2
- package/src/dwn-server.ts +17 -30
- package/src/http-api.ts +499 -396
- package/src/json-rpc-handlers/dwn/process-message.ts +9 -10
- package/src/json-rpc-handlers/subscription/close.ts +4 -4
- package/src/json-rpc-socket.ts +3 -2
- package/src/lib/json-rpc-router.ts +5 -6
- package/src/lib/json-rpc.ts +6 -6
- package/src/metrics.ts +7 -7
- package/src/process-handlers.ts +5 -5
- package/src/registration/proof-of-work-manager.ts +11 -10
- package/src/registration/registration-manager.ts +23 -21
- package/src/registration/registration-store.ts +8 -7
- package/src/storage.ts +15 -13
- package/src/web5-connect/sql-ttl-cache.ts +5 -4
- package/src/web5-connect/web5-connect-server.ts +9 -8
- package/src/ws-api.ts +11 -26
- package/dist/cjs/index.js +0 -6811
- package/dist/cjs/package.json +0 -1
- package/dist/esm/src/lib/http-server-shutdown-handler.d.ts +0 -10
- package/dist/esm/src/lib/http-server-shutdown-handler.d.ts.map +0 -1
- package/dist/esm/src/lib/http-server-shutdown-handler.js +0 -65
- package/dist/esm/src/lib/http-server-shutdown-handler.js.map +0 -1
- package/src/lib/http-server-shutdown-handler.ts +0 -79
package/dist/esm/src/http-api.js
CHANGED
|
@@ -1,26 +1,23 @@
|
|
|
1
|
-
import { DateSort, RecordsRead, RecordsQuery, ProtocolsQuery } from '@enbox/dwn-sdk-js';
|
|
2
|
-
import cors from 'cors';
|
|
3
|
-
import express from 'express';
|
|
4
|
-
import { readFileSync } from 'fs';
|
|
5
|
-
import http from 'http';
|
|
6
1
|
import log from 'loglevel';
|
|
2
|
+
import { Convert } from '@enbox/common';
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
7
4
|
import { register } from 'prom-client';
|
|
8
|
-
import responseTime from 'response-time';
|
|
9
5
|
import { v4 as uuidv4 } from 'uuid';
|
|
6
|
+
import { DataStream, DateSort, ProtocolsQuery, RecordsQuery, RecordsRead } from '@enbox/dwn-sdk-js';
|
|
10
7
|
import { config } from './config.js';
|
|
11
8
|
import { jsonRpcRouter } from './json-rpc-api.js';
|
|
12
9
|
import { Web5ConnectServer } from './web5-connect/web5-connect-server.js';
|
|
13
10
|
import { createJsonRpcErrorResponse, JsonRpcErrorCodes } from './lib/json-rpc.js';
|
|
14
11
|
import { requestCounter, responseHistogram } from './metrics.js';
|
|
15
|
-
import { Convert } from '@enbox/common';
|
|
16
12
|
export class HttpApi {
|
|
17
13
|
#config;
|
|
18
14
|
#packageInfo;
|
|
19
|
-
#api;
|
|
20
15
|
#server;
|
|
21
16
|
web5ConnectServer;
|
|
22
17
|
registrationManager;
|
|
23
18
|
dwn;
|
|
19
|
+
/** Called by WsApi/ConnectionManager when a new WS connection is established. */
|
|
20
|
+
onWebSocketConnection;
|
|
24
21
|
constructor() { }
|
|
25
22
|
static async create(config, dwn, registrationManager) {
|
|
26
23
|
const httpApi = new HttpApi();
|
|
@@ -29,446 +26,524 @@ export class HttpApi {
|
|
|
29
26
|
server: config.serverName,
|
|
30
27
|
};
|
|
31
28
|
try {
|
|
32
|
-
// We populate the `version` and `sdkVersion` properties from the `package.json` file.
|
|
33
29
|
const packageJson = JSON.parse(readFileSync(config.packageJsonPath).toString());
|
|
34
30
|
httpApi.#packageInfo.version = packageJson.version;
|
|
35
|
-
httpApi.#packageInfo.sdkVersion = packageJson.dependencies
|
|
31
|
+
httpApi.#packageInfo.sdkVersion = packageJson.dependencies
|
|
32
|
+
? packageJson.dependencies['@enbox/dwn-sdk-js']
|
|
33
|
+
: undefined;
|
|
36
34
|
}
|
|
37
35
|
catch (error) {
|
|
38
36
|
log.info('could not read `package.json` for version info', error);
|
|
39
37
|
}
|
|
40
38
|
httpApi.#config = config;
|
|
41
|
-
httpApi.#api = express();
|
|
42
|
-
httpApi.#server = http.createServer(httpApi.#api);
|
|
43
39
|
httpApi.dwn = dwn;
|
|
44
40
|
if (registrationManager !== undefined) {
|
|
45
41
|
httpApi.registrationManager = registrationManager;
|
|
46
42
|
}
|
|
47
|
-
// create the Web5 Connect Server
|
|
48
43
|
httpApi.web5ConnectServer = await Web5ConnectServer.create({
|
|
49
44
|
baseUrl: config.baseUrl,
|
|
50
45
|
sqlTtlCacheUrl: config.ttlCacheUrl,
|
|
51
46
|
});
|
|
52
|
-
httpApi.#setupMiddleware();
|
|
53
|
-
httpApi.#setupRoutes();
|
|
54
47
|
return httpApi;
|
|
55
48
|
}
|
|
56
49
|
get server() {
|
|
57
50
|
return this.#server;
|
|
58
51
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
this
|
|
64
|
-
this.#
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Configures the HTTP server's request handlers.
|
|
81
|
-
*/
|
|
82
|
-
#setupRoutes() {
|
|
83
|
-
const leadTailSlashRegex = /^\/|\/$/;
|
|
84
|
-
function readReplyHandler(res, reply) {
|
|
85
|
-
if (reply.status.code === 200) {
|
|
86
|
-
if (reply?.entry?.data) {
|
|
87
|
-
const stream = reply.entry.data;
|
|
88
|
-
res.setHeader('content-type', reply.entry.recordsWrite.descriptor.dataFormat);
|
|
89
|
-
res.setHeader('dwn-response', JSON.stringify(reply));
|
|
90
|
-
return stream.pipe(res);
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// HTTP request handler
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
async start(port) {
|
|
56
|
+
const self = this; // capture for closures
|
|
57
|
+
this.#server = Bun.serve({
|
|
58
|
+
port,
|
|
59
|
+
async fetch(req, server) {
|
|
60
|
+
const startTime = performance.now();
|
|
61
|
+
const url = new URL(req.url);
|
|
62
|
+
const path = url.pathname;
|
|
63
|
+
const method = req.method;
|
|
64
|
+
// --- WebSocket upgrade ---
|
|
65
|
+
if (method === 'GET' && req.headers.get('upgrade') === 'websocket') {
|
|
66
|
+
const upgraded = server.upgrade(req, { data: { connection: null } });
|
|
67
|
+
if (upgraded) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
return new Response('WebSocket upgrade failed', { status: 400 });
|
|
91
71
|
}
|
|
92
|
-
|
|
93
|
-
|
|
72
|
+
// --- Route matching ---
|
|
73
|
+
let response;
|
|
74
|
+
try {
|
|
75
|
+
response = await self.#route(req, url, path, method);
|
|
94
76
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
77
|
+
catch (error) {
|
|
78
|
+
log.error(`Unhandled error on ${method} ${path}:`, error);
|
|
79
|
+
response = new Response('Internal Server Error', { status: 500 });
|
|
80
|
+
}
|
|
81
|
+
// --- CORS headers ---
|
|
82
|
+
response.headers.set('access-control-allow-origin', '*');
|
|
83
|
+
response.headers.set('access-control-allow-methods', 'GET, POST, OPTIONS');
|
|
84
|
+
response.headers.set('access-control-allow-headers', '*');
|
|
85
|
+
response.headers.set('access-control-expose-headers', 'dwn-response');
|
|
86
|
+
// --- Response-time metrics ---
|
|
87
|
+
const elapsed = performance.now() - startTime;
|
|
88
|
+
const routeLabel = (method + (path === '/' ? '/jsonrpc' : path))
|
|
89
|
+
.toLowerCase()
|
|
90
|
+
.replace(/[:.]/g, '')
|
|
91
|
+
.replace(/\//g, '_');
|
|
92
|
+
responseHistogram.labels(routeLabel, String(response.status)).observe(elapsed);
|
|
93
|
+
log.info(method, decodeURI(path), response.status);
|
|
94
|
+
return response;
|
|
95
|
+
},
|
|
96
|
+
websocket: {
|
|
97
|
+
open(ws) {
|
|
98
|
+
if (self.onWebSocketConnection) {
|
|
99
|
+
self.onWebSocketConnection(ws);
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
message(ws, msg) {
|
|
103
|
+
const connection = ws.data?.connection;
|
|
104
|
+
if (connection) {
|
|
105
|
+
connection.message(typeof msg === 'string' ? Buffer.from(msg) : msg);
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
close(ws) {
|
|
109
|
+
const connection = ws.data?.connection;
|
|
110
|
+
if (connection) {
|
|
111
|
+
connection.close();
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
// Bun automatically responds to pings with pongs
|
|
115
|
+
},
|
|
106
116
|
});
|
|
107
|
-
|
|
117
|
+
}
|
|
118
|
+
async close() {
|
|
119
|
+
if (this.#server) {
|
|
120
|
+
this.#server.stop(true); // close all connections immediately
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
// Router
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
async #route(req, url, path, method) {
|
|
127
|
+
// --- CORS preflight ---
|
|
128
|
+
if (method === 'OPTIONS') {
|
|
129
|
+
return new Response(null, { status: 204 });
|
|
130
|
+
}
|
|
131
|
+
// --- Static routes ---
|
|
132
|
+
if (method === 'GET' && path === '/health') {
|
|
133
|
+
return Response.json({ ok: true });
|
|
134
|
+
}
|
|
135
|
+
if (method === 'GET' && path === '/metrics') {
|
|
108
136
|
try {
|
|
109
|
-
|
|
110
|
-
|
|
137
|
+
const metricsBody = await register.metrics();
|
|
138
|
+
return new Response(metricsBody, {
|
|
139
|
+
headers: { 'content-type': register.contentType },
|
|
140
|
+
});
|
|
111
141
|
}
|
|
112
142
|
catch (e) {
|
|
113
|
-
|
|
143
|
+
return new Response(String(e), { status: 500 });
|
|
114
144
|
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
145
|
+
}
|
|
146
|
+
if (method === 'GET' && path === '/') {
|
|
147
|
+
return new Response('please use am enbox client, for example: https://github.com/enboxorg/enbox ', { headers: { 'content-type': 'text/plain' } });
|
|
148
|
+
}
|
|
149
|
+
if (method === 'GET' && path === '/info') {
|
|
150
|
+
return this.#handleInfo();
|
|
151
|
+
}
|
|
152
|
+
// --- JSON-RPC POST ---
|
|
153
|
+
if (method === 'POST' && path === '/') {
|
|
154
|
+
return this.#handleJsonRpcPost(req);
|
|
155
|
+
}
|
|
156
|
+
// --- Registration routes ---
|
|
157
|
+
const registrationResponse = await this.#matchRegistrationRoutes(req, path, method);
|
|
158
|
+
if (registrationResponse) {
|
|
159
|
+
return registrationResponse;
|
|
160
|
+
}
|
|
161
|
+
// --- Web5 Connect routes ---
|
|
162
|
+
const connectResponse = await this.#matchWeb5ConnectRoutes(req, path, method);
|
|
163
|
+
if (connectResponse) {
|
|
164
|
+
return connectResponse;
|
|
165
|
+
}
|
|
166
|
+
// --- DID routes (parameterized) ---
|
|
167
|
+
return this.#matchDidRoutes(req, url, path);
|
|
168
|
+
}
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
// DID convenience routes
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
async #matchDidRoutes(req, url, path) {
|
|
173
|
+
const leadTailSlashRegex = /^\/|\/$/g;
|
|
174
|
+
// /:did/read/protocols/:protocol/* (also matches trailing slash with empty path)
|
|
175
|
+
{
|
|
176
|
+
const match = path.match(/^\/([^/]+)\/read\/protocols\/([^/]+)\/(.*)$/);
|
|
177
|
+
if (match && req.method === 'GET') {
|
|
178
|
+
const [, did, protocolParam, protocolPathRaw] = match;
|
|
179
|
+
return this.#handleReadProtocolRecord(did, protocolParam, protocolPathRaw, url, leadTailSlashRegex);
|
|
120
180
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
lastLevelObject[lastKey] = req.query[param];
|
|
129
|
-
}
|
|
130
|
-
// the protocol path segment is base64url encoded, as the actual protocol is a URL
|
|
131
|
-
// we decode it here in order to filter for the correct protocol
|
|
132
|
-
const protocol = Convert.base64Url(req.params.protocol).toString();
|
|
133
|
-
queryOptions.filter.protocol = protocol;
|
|
134
|
-
queryOptions.filter.protocolPath = req.params[0].replace(leadTailSlashRegex, '');
|
|
135
|
-
const query = await RecordsQuery.create({
|
|
136
|
-
filter: queryOptions.filter,
|
|
137
|
-
pagination: { limit: 1 },
|
|
138
|
-
dateSort: DateSort.PublishedDescending
|
|
139
|
-
});
|
|
140
|
-
const { entries, status } = await this.dwn.processMessage(req.params.did, query.message);
|
|
141
|
-
if (status.code === 200) {
|
|
142
|
-
if (entries[0]) {
|
|
143
|
-
const record = await RecordsRead.create({
|
|
144
|
-
filter: { recordId: entries[0].recordId },
|
|
145
|
-
});
|
|
146
|
-
const reply = await this.dwn.processMessage(req.params.did, record.toJSON());
|
|
147
|
-
return readReplyHandler(res, reply);
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
return res.sendStatus(404);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
else if (status.code === 401) {
|
|
154
|
-
return res.sendStatus(404);
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
return res.sendStatus(status.code);
|
|
158
|
-
}
|
|
181
|
+
}
|
|
182
|
+
// /:did/read/protocols/:protocol
|
|
183
|
+
{
|
|
184
|
+
const match = path.match(/^\/([^/]+)\/read\/protocols\/([^/]+)$/);
|
|
185
|
+
if (match && req.method === 'GET') {
|
|
186
|
+
const [, did, protocolParam] = match;
|
|
187
|
+
return this.#handleReadProtocol(did, protocolParam);
|
|
159
188
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
189
|
+
}
|
|
190
|
+
// /:did/read/records/:id OR /:did/records/:id
|
|
191
|
+
{
|
|
192
|
+
const match = path.match(/^\/([^/]+)\/(?:read\/)?records\/([^/]+)$/);
|
|
193
|
+
if (match && req.method === 'GET') {
|
|
194
|
+
const [, did, recordId] = match;
|
|
195
|
+
return this.#handleReadRecord(did, recordId);
|
|
163
196
|
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const query = await ProtocolsQuery.create({
|
|
172
|
-
filter: { protocol }
|
|
173
|
-
});
|
|
174
|
-
const { entries, status } = await this.dwn.processMessage(req.params.did, query.message);
|
|
175
|
-
if (status.code === 200) {
|
|
176
|
-
if (entries.length) {
|
|
177
|
-
res.status(status.code);
|
|
178
|
-
res.json(entries[0]);
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
return res.sendStatus(404);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
else if (status.code === 401) {
|
|
185
|
-
return res.sendStatus(404);
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
return res.sendStatus(status.code);
|
|
189
|
-
}
|
|
197
|
+
}
|
|
198
|
+
// /:did/query/protocols
|
|
199
|
+
{
|
|
200
|
+
const match = path.match(/^\/([^/]+)\/query\/protocols$/);
|
|
201
|
+
if (match && req.method === 'GET') {
|
|
202
|
+
const [, did] = match;
|
|
203
|
+
return this.#handleQueryProtocols(did);
|
|
190
204
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
205
|
+
}
|
|
206
|
+
// /:did/query
|
|
207
|
+
{
|
|
208
|
+
const match = path.match(/^\/([^/]+)\/query$/);
|
|
209
|
+
if (match && req.method === 'GET') {
|
|
210
|
+
const [, did] = match;
|
|
211
|
+
return this.#handleQueryRecords(did, url);
|
|
194
212
|
}
|
|
213
|
+
}
|
|
214
|
+
return new Response('Not Found', { status: 404 });
|
|
215
|
+
}
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
// Handlers
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
#handleInfo() {
|
|
220
|
+
const registrationRequirements = [];
|
|
221
|
+
if (config.registrationProofOfWorkEnabled) {
|
|
222
|
+
registrationRequirements.push('proof-of-work-sha256-v0');
|
|
223
|
+
}
|
|
224
|
+
if (config.termsOfServiceFilePath !== undefined) {
|
|
225
|
+
registrationRequirements.push('terms-of-service');
|
|
226
|
+
}
|
|
227
|
+
return Response.json({
|
|
228
|
+
url: config.baseUrl,
|
|
229
|
+
server: this.#packageInfo.server,
|
|
230
|
+
maxFileSize: config.maxRecordDataSize,
|
|
231
|
+
registrationRequirements: registrationRequirements,
|
|
232
|
+
version: this.#packageInfo.version,
|
|
233
|
+
sdkVersion: this.#packageInfo.sdkVersion,
|
|
234
|
+
webSocketSupport: config.webSocketSupport,
|
|
195
235
|
});
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const reply =
|
|
201
|
-
return
|
|
236
|
+
}
|
|
237
|
+
async #handleJsonRpcPost(req) {
|
|
238
|
+
const dwnRpcRequestString = req.headers.get('dwn-request');
|
|
239
|
+
if (!dwnRpcRequestString) {
|
|
240
|
+
const reply = createJsonRpcErrorResponse(uuidv4(), JsonRpcErrorCodes.BadRequest, 'request payload required.');
|
|
241
|
+
return Response.json(reply, { status: 400 });
|
|
242
|
+
}
|
|
243
|
+
let dwnRpcRequest;
|
|
244
|
+
try {
|
|
245
|
+
dwnRpcRequest = JSON.parse(dwnRpcRequestString);
|
|
246
|
+
}
|
|
247
|
+
catch (e) {
|
|
248
|
+
const reply = createJsonRpcErrorResponse(uuidv4(), JsonRpcErrorCodes.BadRequest, e.message);
|
|
249
|
+
return Response.json(reply, { status: 400 });
|
|
250
|
+
}
|
|
251
|
+
// Read the request body into bytes first, then wrap in a fresh ReadableStream.
|
|
252
|
+
// Bun's native Request.body stream has an incompatible reader.releaseLock()
|
|
253
|
+
// that breaks DWN SDK's DataStream.toBytes(), so we materialise the body here.
|
|
254
|
+
const contentLength = req.headers.get('content-length');
|
|
255
|
+
const transferEncoding = req.headers.get('transfer-encoding');
|
|
256
|
+
let requestDataStream;
|
|
257
|
+
if (parseInt(contentLength ?? '0') > 0 || transferEncoding !== null) {
|
|
258
|
+
const bodyBytes = new Uint8Array(await req.arrayBuffer());
|
|
259
|
+
requestDataStream = DataStream.fromBytes(bodyBytes);
|
|
260
|
+
}
|
|
261
|
+
const requestContext = {
|
|
262
|
+
dwn: this.dwn,
|
|
263
|
+
transport: 'http',
|
|
264
|
+
dataStream: requestDataStream,
|
|
202
265
|
};
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
else if (status.code === 401) {
|
|
213
|
-
return res.sendStatus(404);
|
|
214
|
-
}
|
|
215
|
-
else {
|
|
216
|
-
return res.sendStatus(status.code);
|
|
217
|
-
}
|
|
266
|
+
const { jsonRpcResponse, dataStream: responseDataStream } = await jsonRpcRouter.handle(dwnRpcRequest, requestContext);
|
|
267
|
+
if (jsonRpcResponse.error) {
|
|
268
|
+
requestCounter.inc({ method: dwnRpcRequest.method, error: 1 });
|
|
269
|
+
return Response.json(jsonRpcResponse, { status: 500 });
|
|
270
|
+
}
|
|
271
|
+
requestCounter.inc({
|
|
272
|
+
method: dwnRpcRequest.method,
|
|
273
|
+
status: jsonRpcResponse?.result?.reply?.status?.code || 0,
|
|
218
274
|
});
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
dateSort: recordsQueryOptions.dateSort,
|
|
275
|
+
if (responseDataStream) {
|
|
276
|
+
return new Response(responseDataStream, {
|
|
277
|
+
headers: {
|
|
278
|
+
'content-type': 'application/octet-stream',
|
|
279
|
+
'dwn-response': JSON.stringify(jsonRpcResponse),
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
return Response.json(jsonRpcResponse);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
#readReplyToResponse(reply) {
|
|
288
|
+
if (reply.status.code === 200) {
|
|
289
|
+
if (reply?.entry?.data) {
|
|
290
|
+
return new Response(reply.entry.data, {
|
|
291
|
+
headers: {
|
|
292
|
+
'content-type': reply.entry.recordsWrite.descriptor.dataFormat,
|
|
293
|
+
'dwn-response': JSON.stringify(reply),
|
|
294
|
+
},
|
|
240
295
|
});
|
|
241
|
-
// should always return a 200 status code with a JSON response
|
|
242
|
-
const reply = await this.dwn.processMessage(req.params.did, recordsQuery.message);
|
|
243
|
-
res.setHeader('content-type', 'application/json');
|
|
244
|
-
return res.json(reply);
|
|
245
296
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
return res.status(400).send(error);
|
|
297
|
+
else {
|
|
298
|
+
return new Response(null, { status: 400 });
|
|
249
299
|
}
|
|
300
|
+
}
|
|
301
|
+
else if (reply.status.code === 401) {
|
|
302
|
+
return new Response(null, { status: 404 });
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
return Response.json(reply, { status: reply.status.code });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
async #handleReadRecord(did, recordId) {
|
|
309
|
+
const record = await RecordsRead.create({
|
|
310
|
+
filter: { recordId },
|
|
250
311
|
});
|
|
251
|
-
this
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
312
|
+
const reply = await this.dwn.processMessage(did, record.message);
|
|
313
|
+
return this.#readReplyToResponse(reply);
|
|
314
|
+
}
|
|
315
|
+
async #handleReadProtocolRecord(did, protocolParam, protocolPathRaw, url, leadTailSlashRegex) {
|
|
316
|
+
if (!protocolPathRaw || protocolPathRaw.replace(leadTailSlashRegex, '') === '') {
|
|
317
|
+
return new Response('protocol path is required', { status: 400 });
|
|
318
|
+
}
|
|
319
|
+
try {
|
|
320
|
+
const queryOptions = { filter: {} };
|
|
321
|
+
for (const [param, value] of url.searchParams) {
|
|
322
|
+
const keys = param.split('.');
|
|
323
|
+
const lastKey = keys.pop();
|
|
324
|
+
const nestObj = (obj, key) => obj[key] = obj[key] || {};
|
|
325
|
+
const lastLevelObject = keys.reduce(nestObj, queryOptions);
|
|
326
|
+
lastLevelObject[lastKey] = value;
|
|
261
327
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
328
|
+
const protocol = Convert.base64Url(protocolParam).toString();
|
|
329
|
+
queryOptions.filter.protocol = protocol;
|
|
330
|
+
queryOptions.filter.protocolPath = protocolPathRaw.replace(leadTailSlashRegex, '');
|
|
331
|
+
const query = await RecordsQuery.create({
|
|
332
|
+
filter: queryOptions.filter,
|
|
333
|
+
pagination: { limit: 1 },
|
|
334
|
+
dateSort: DateSort.PublishedDescending,
|
|
335
|
+
});
|
|
336
|
+
const { entries, status } = await this.dwn.processMessage(did, query.message);
|
|
337
|
+
if (status.code === 200) {
|
|
338
|
+
if (entries[0]) {
|
|
339
|
+
const record = await RecordsRead.create({
|
|
340
|
+
filter: { recordId: entries[0].recordId },
|
|
341
|
+
});
|
|
342
|
+
const reply = await this.dwn.processMessage(did, record.toJSON());
|
|
343
|
+
return this.#readReplyToResponse(reply);
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
return new Response(null, { status: 404 });
|
|
347
|
+
}
|
|
265
348
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
return res.status(400).json(reply);
|
|
349
|
+
else if (status.code === 401) {
|
|
350
|
+
return new Response(null, { status: 404 });
|
|
269
351
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const transferEncoding = req.headers['transfer-encoding'];
|
|
273
|
-
const requestDataStream = parseInt(contentLength) > 0 || transferEncoding !== undefined ? req : undefined;
|
|
274
|
-
const requestContext = {
|
|
275
|
-
dwn: this.dwn,
|
|
276
|
-
transport: 'http',
|
|
277
|
-
dataStream: requestDataStream,
|
|
278
|
-
};
|
|
279
|
-
const { jsonRpcResponse, dataStream: responseDataStream } = await jsonRpcRouter.handle(dwnRpcRequest, requestContext);
|
|
280
|
-
// If the handler catches a thrown exception and returns a JSON RPC InternalError, return the equivalent
|
|
281
|
-
// HTTP 500 Internal Server Error with the response.
|
|
282
|
-
if (jsonRpcResponse.error) {
|
|
283
|
-
requestCounter.inc({ method: dwnRpcRequest.method, error: 1 });
|
|
284
|
-
return res.status(500).json(jsonRpcResponse);
|
|
352
|
+
else {
|
|
353
|
+
return new Response(null, { status: status.code });
|
|
285
354
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
log.error(`Error processing request: ${decodeURI(url.pathname)}`, error);
|
|
358
|
+
return new Response('Bad Request', { status: 400 });
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
async #handleReadProtocol(did, protocolParam) {
|
|
362
|
+
try {
|
|
363
|
+
const protocol = Convert.base64Url(protocolParam).toString();
|
|
364
|
+
const query = await ProtocolsQuery.create({
|
|
365
|
+
filter: { protocol },
|
|
289
366
|
});
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
367
|
+
const { entries, status } = await this.dwn.processMessage(did, query.message);
|
|
368
|
+
if (status.code === 200) {
|
|
369
|
+
if (entries.length) {
|
|
370
|
+
return Response.json(entries[0], { status: status.code });
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
return new Response(null, { status: 404 });
|
|
374
|
+
}
|
|
294
375
|
}
|
|
295
|
-
else {
|
|
296
|
-
return
|
|
376
|
+
else if (status.code === 401) {
|
|
377
|
+
return new Response(null, { status: 404 });
|
|
297
378
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
this.#api.get('/info', (req, res) => {
|
|
301
|
-
res.setHeader('content-type', 'application/json');
|
|
302
|
-
const registrationRequirements = [];
|
|
303
|
-
if (config.registrationProofOfWorkEnabled) {
|
|
304
|
-
registrationRequirements.push('proof-of-work-sha256-v0');
|
|
379
|
+
else {
|
|
380
|
+
return new Response(null, { status: status.code });
|
|
305
381
|
}
|
|
306
|
-
|
|
307
|
-
|
|
382
|
+
}
|
|
383
|
+
catch (error) {
|
|
384
|
+
log.error(`Error processing request`, error);
|
|
385
|
+
return new Response('Bad Request', { status: 400 });
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
async #handleQueryProtocols(did) {
|
|
389
|
+
const query = await ProtocolsQuery.create({});
|
|
390
|
+
const { entries, status } = await this.dwn.processMessage(did, query.message);
|
|
391
|
+
if (status.code === 200) {
|
|
392
|
+
return Response.json(entries, { status: status.code });
|
|
393
|
+
}
|
|
394
|
+
else if (status.code === 401) {
|
|
395
|
+
return new Response(null, { status: 404 });
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
return new Response(null, { status: status.code });
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
async #handleQueryRecords(did, url) {
|
|
402
|
+
try {
|
|
403
|
+
const recordsQueryOptions = {};
|
|
404
|
+
for (const [param, value] of url.searchParams) {
|
|
405
|
+
const keys = param.split('.');
|
|
406
|
+
const lastKey = keys.pop();
|
|
407
|
+
const nestObj = (obj, key) => obj[key] = obj[key] || {};
|
|
408
|
+
const lastLevelObject = keys.reduce(nestObj, recordsQueryOptions);
|
|
409
|
+
lastLevelObject[lastKey] = value;
|
|
308
410
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
registrationRequirements: registrationRequirements,
|
|
314
|
-
version: this.#packageInfo.version,
|
|
315
|
-
sdkVersion: this.#packageInfo.sdkVersion,
|
|
316
|
-
webSocketSupport: config.webSocketSupport,
|
|
411
|
+
const recordsQuery = await RecordsQuery.create({
|
|
412
|
+
filter: recordsQueryOptions.filter,
|
|
413
|
+
pagination: recordsQueryOptions.pagination,
|
|
414
|
+
dateSort: recordsQueryOptions.dateSort,
|
|
317
415
|
});
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
#setupRegistrationRoutes() {
|
|
322
|
-
if (this.#config.registrationProofOfWorkEnabled) {
|
|
323
|
-
this.#api.get('/registration/proof-of-work', async (_req, res) => {
|
|
324
|
-
const proofOfWorkChallenge = this.registrationManager.getProofOfWorkChallenge();
|
|
325
|
-
res.json(proofOfWorkChallenge);
|
|
416
|
+
const reply = await this.dwn.processMessage(did, recordsQuery.message);
|
|
417
|
+
return Response.json(reply, {
|
|
418
|
+
headers: { 'content-type': 'application/json' },
|
|
326
419
|
});
|
|
327
420
|
}
|
|
328
|
-
|
|
329
|
-
|
|
421
|
+
catch (error) {
|
|
422
|
+
return Response.json(error, { status: 400 });
|
|
330
423
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
424
|
+
}
|
|
425
|
+
// ---------------------------------------------------------------------------
|
|
426
|
+
// Registration routes
|
|
427
|
+
// ---------------------------------------------------------------------------
|
|
428
|
+
async #matchRegistrationRoutes(req, path, method) {
|
|
429
|
+
if (method === 'GET' && path === '/registration/proof-of-work'
|
|
430
|
+
&& this.#config.registrationProofOfWorkEnabled) {
|
|
431
|
+
const proofOfWorkChallenge = this.registrationManager.getProofOfWorkChallenge();
|
|
432
|
+
return Response.json(proofOfWorkChallenge);
|
|
433
|
+
}
|
|
434
|
+
if (method === 'GET' && path === '/registration/terms-of-service'
|
|
435
|
+
&& this.#config.termsOfServiceFilePath !== undefined) {
|
|
436
|
+
return new Response(this.registrationManager.getTermsOfService());
|
|
437
|
+
}
|
|
438
|
+
if (method === 'POST' && path === '/registration'
|
|
439
|
+
&& this.#config.registrationStoreUrl !== undefined) {
|
|
440
|
+
const requestBody = await req.json();
|
|
441
|
+
log.info('Registration request:', requestBody);
|
|
442
|
+
try {
|
|
443
|
+
await this.registrationManager.handleRegistrationRequest(requestBody);
|
|
444
|
+
return Response.json({ success: true }, { status: 200 });
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
const dwnServerError = error;
|
|
448
|
+
if (dwnServerError.code !== undefined) {
|
|
449
|
+
return Response.json(dwnServerError, { status: 400 });
|
|
338
450
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
res.status(400).json(dwnServerError);
|
|
343
|
-
}
|
|
344
|
-
else {
|
|
345
|
-
log.info('Error handling registration request:', error);
|
|
346
|
-
res.status(500).json({ success: false });
|
|
347
|
-
}
|
|
451
|
+
else {
|
|
452
|
+
log.info('Error handling registration request:', error);
|
|
453
|
+
return Response.json({ success: false }, { status: 500 });
|
|
348
454
|
}
|
|
349
|
-
}
|
|
455
|
+
}
|
|
350
456
|
}
|
|
457
|
+
return null;
|
|
351
458
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
*/
|
|
359
|
-
this.#api.post('/connect/par', async (req, res) => {
|
|
459
|
+
// ---------------------------------------------------------------------------
|
|
460
|
+
// Web5 Connect routes
|
|
461
|
+
// ---------------------------------------------------------------------------
|
|
462
|
+
async #matchWeb5ConnectRoutes(req, path, method) {
|
|
463
|
+
// POST /connect/par
|
|
464
|
+
if (method === 'POST' && path === '/connect/par') {
|
|
360
465
|
log.info('Storing Pushed Authorization Request (PAR) request...');
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
return res.status(400).json({
|
|
365
|
-
ok: false,
|
|
366
|
-
status: {
|
|
367
|
-
code: 400,
|
|
368
|
-
message: "Bad Request: Missing 'request' parameter",
|
|
369
|
-
},
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
// Validate that `request_uri` was NOT provided
|
|
373
|
-
if (req.body?.request?.request_uri) {
|
|
374
|
-
return res.status(400).json({
|
|
466
|
+
const body = await req.json();
|
|
467
|
+
if (!body.request) {
|
|
468
|
+
return Response.json({
|
|
375
469
|
ok: false,
|
|
376
|
-
status: {
|
|
377
|
-
|
|
378
|
-
message: "Bad Request: 'request_uri' parameter is not allowed in PAR",
|
|
379
|
-
},
|
|
380
|
-
});
|
|
470
|
+
status: { code: 400, message: 'Bad Request: Missing \'request\' parameter' },
|
|
471
|
+
}, { status: 400 });
|
|
381
472
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
});
|
|
385
|
-
/**
|
|
386
|
-
* Endpoint for the Provider to retrieve the Authorization Request from the request_uri
|
|
387
|
-
*/
|
|
388
|
-
this.#api.get('/connect/authorize/:requestId.jwt', async (req, res) => {
|
|
389
|
-
log.info(`Retrieving Web5 Connect Request object of ID: ${req.params.requestId}...`);
|
|
390
|
-
// Look up the request object based on the requestId.
|
|
391
|
-
const requestObjectJwt = await this.web5ConnectServer.getWeb5ConnectRequest(req.params.requestId);
|
|
392
|
-
if (!requestObjectJwt) {
|
|
393
|
-
res.status(404).json({
|
|
473
|
+
if (body?.request?.request_uri) {
|
|
474
|
+
return Response.json({
|
|
394
475
|
ok: false,
|
|
395
|
-
status: { code:
|
|
396
|
-
});
|
|
476
|
+
status: { code: 400, message: 'Bad Request: \'request_uri\' parameter is not allowed in PAR' },
|
|
477
|
+
}, { status: 400 });
|
|
397
478
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
479
|
+
const result = await this.web5ConnectServer.setWeb5ConnectRequest(body.request);
|
|
480
|
+
return Response.json(result, { status: 201 });
|
|
481
|
+
}
|
|
482
|
+
// GET /connect/authorize/:requestId.jwt
|
|
483
|
+
{
|
|
484
|
+
const match = path.match(/^\/connect\/authorize\/([^/]+)\.jwt$/);
|
|
485
|
+
if (match && method === 'GET') {
|
|
486
|
+
const requestId = match[1];
|
|
487
|
+
log.info(`Retrieving Web5 Connect Request object of ID: ${requestId}...`);
|
|
488
|
+
const requestObjectJwt = await this.web5ConnectServer.getWeb5ConnectRequest(requestId);
|
|
489
|
+
if (!requestObjectJwt) {
|
|
490
|
+
return Response.json({
|
|
491
|
+
ok: false,
|
|
492
|
+
status: { code: 404, message: 'Not Found' },
|
|
493
|
+
}, { status: 404 });
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
const body = typeof requestObjectJwt === 'string'
|
|
497
|
+
? requestObjectJwt
|
|
498
|
+
: JSON.stringify(requestObjectJwt);
|
|
499
|
+
return new Response(body, {
|
|
500
|
+
headers: { 'content-type': 'application/jwt' },
|
|
501
|
+
});
|
|
502
|
+
}
|
|
401
503
|
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
*/
|
|
406
|
-
this.#api.post('/connect/callback', async (req, res) => {
|
|
504
|
+
}
|
|
505
|
+
// POST /connect/callback
|
|
506
|
+
if (method === 'POST' && path === '/connect/callback') {
|
|
407
507
|
log.info('Storing Identity Provider (wallet) pushed response with ID token...');
|
|
408
|
-
|
|
409
|
-
const idToken =
|
|
410
|
-
const state =
|
|
508
|
+
const body = await req.json();
|
|
509
|
+
const idToken = body.id_token;
|
|
510
|
+
const state = body.state;
|
|
411
511
|
if (idToken !== undefined && state != undefined) {
|
|
412
512
|
await this.web5ConnectServer.setWeb5ConnectResponse(state, idToken);
|
|
413
|
-
|
|
513
|
+
return Response.json({
|
|
414
514
|
ok: true,
|
|
415
|
-
status: { code: 201, message: 'Created' }
|
|
416
|
-
});
|
|
515
|
+
status: { code: 201, message: 'Created' },
|
|
516
|
+
}, { status: 201 });
|
|
417
517
|
}
|
|
418
518
|
else {
|
|
419
|
-
|
|
519
|
+
return Response.json({
|
|
420
520
|
ok: false,
|
|
421
|
-
status: { code: 400, message: 'Bad Request' }
|
|
422
|
-
});
|
|
521
|
+
status: { code: 400, message: 'Bad Request' },
|
|
522
|
+
}, { status: 400 });
|
|
423
523
|
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
}
|
|
438
|
-
else {
|
|
439
|
-
res.set('Content-Type', 'application/jwt');
|
|
440
|
-
res.send(idToken);
|
|
441
|
-
}
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
/**
|
|
445
|
-
* Starts the HTTP API endpoint on the given port.
|
|
446
|
-
* @returns The HTTP server instance.
|
|
447
|
-
*/
|
|
448
|
-
async start(port) {
|
|
449
|
-
// promisify http.Server.listen() and await on it
|
|
450
|
-
await new Promise((resolve) => {
|
|
451
|
-
this.#server.listen(port, () => {
|
|
452
|
-
resolve();
|
|
453
|
-
});
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
/**
|
|
457
|
-
* Stops the HTTP API endpoint.
|
|
458
|
-
*/
|
|
459
|
-
async close() {
|
|
460
|
-
// promisify http.Server.close() and await on it
|
|
461
|
-
await new Promise((resolve, reject) => {
|
|
462
|
-
this.#server.close((err) => {
|
|
463
|
-
if (err) {
|
|
464
|
-
reject(err);
|
|
524
|
+
}
|
|
525
|
+
// GET /connect/token/:state.jwt
|
|
526
|
+
{
|
|
527
|
+
const match = path.match(/^\/connect\/token\/([^/]+)\.jwt$/);
|
|
528
|
+
if (match && method === 'GET') {
|
|
529
|
+
const state = match[1];
|
|
530
|
+
log.info(`Retrieving ID token for state: ${state}...`);
|
|
531
|
+
const idToken = await this.web5ConnectServer.getWeb5ConnectResponse(state);
|
|
532
|
+
if (!idToken) {
|
|
533
|
+
return Response.json({
|
|
534
|
+
ok: false,
|
|
535
|
+
status: { code: 404, message: 'Not Found' },
|
|
536
|
+
}, { status: 404 });
|
|
465
537
|
}
|
|
466
538
|
else {
|
|
467
|
-
|
|
539
|
+
const body = typeof idToken === 'string' ? idToken : JSON.stringify(idToken);
|
|
540
|
+
return new Response(body, {
|
|
541
|
+
headers: { 'content-type': 'application/jwt' },
|
|
542
|
+
});
|
|
468
543
|
}
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return null;
|
|
472
547
|
}
|
|
473
548
|
}
|
|
474
549
|
//# sourceMappingURL=http-api.js.map
|