@enbox/dwn-server 0.0.1
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 +201 -0
- package/README.md +353 -0
- package/dist/cjs/index.js +6811 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/src/config.d.ts +55 -0
- package/dist/esm/src/config.d.ts.map +1 -0
- package/dist/esm/src/config.js +60 -0
- package/dist/esm/src/config.js.map +1 -0
- package/dist/esm/src/connection/connection-manager.d.ts +25 -0
- package/dist/esm/src/connection/connection-manager.d.ts.map +1 -0
- package/dist/esm/src/connection/connection-manager.js +26 -0
- package/dist/esm/src/connection/connection-manager.js.map +1 -0
- package/dist/esm/src/connection/socket-connection.d.ts +65 -0
- package/dist/esm/src/connection/socket-connection.d.ts.map +1 -0
- package/dist/esm/src/connection/socket-connection.js +180 -0
- package/dist/esm/src/connection/socket-connection.js.map +1 -0
- package/dist/esm/src/dwn-error.d.ts +29 -0
- package/dist/esm/src/dwn-error.d.ts.map +1 -0
- package/dist/esm/src/dwn-error.js +36 -0
- package/dist/esm/src/dwn-error.js.map +1 -0
- package/dist/esm/src/dwn-server.d.ts +60 -0
- package/dist/esm/src/dwn-server.d.ts.map +1 -0
- package/dist/esm/src/dwn-server.js +130 -0
- package/dist/esm/src/dwn-server.js.map +1 -0
- package/dist/esm/src/http-api.d.ts +26 -0
- package/dist/esm/src/http-api.d.ts.map +1 -0
- package/dist/esm/src/http-api.js +474 -0
- package/dist/esm/src/http-api.js.map +1 -0
- package/dist/esm/src/index.d.ts +7 -0
- package/dist/esm/src/index.d.ts.map +1 -0
- package/dist/esm/src/index.js +6 -0
- package/dist/esm/src/index.js.map +1 -0
- package/dist/esm/src/json-rpc-api.d.ts +3 -0
- package/dist/esm/src/json-rpc-api.d.ts.map +1 -0
- package/dist/esm/src/json-rpc-api.js +8 -0
- package/dist/esm/src/json-rpc-api.js.map +1 -0
- package/dist/esm/src/json-rpc-handlers/dwn/index.d.ts +2 -0
- package/dist/esm/src/json-rpc-handlers/dwn/index.d.ts.map +1 -0
- package/dist/esm/src/json-rpc-handlers/dwn/index.js +2 -0
- package/dist/esm/src/json-rpc-handlers/dwn/index.js.map +1 -0
- package/dist/esm/src/json-rpc-handlers/dwn/process-message.d.ts +3 -0
- package/dist/esm/src/json-rpc-handlers/dwn/process-message.d.ts.map +1 -0
- package/dist/esm/src/json-rpc-handlers/dwn/process-message.js +73 -0
- package/dist/esm/src/json-rpc-handlers/dwn/process-message.js.map +1 -0
- package/dist/esm/src/json-rpc-handlers/subscription/close.d.ts +10 -0
- package/dist/esm/src/json-rpc-handlers/subscription/close.d.ts.map +1 -0
- package/dist/esm/src/json-rpc-handlers/subscription/close.js +39 -0
- package/dist/esm/src/json-rpc-handlers/subscription/close.js.map +1 -0
- package/dist/esm/src/json-rpc-handlers/subscription/index.d.ts +2 -0
- package/dist/esm/src/json-rpc-handlers/subscription/index.d.ts.map +1 -0
- package/dist/esm/src/json-rpc-handlers/subscription/index.js +2 -0
- package/dist/esm/src/json-rpc-handlers/subscription/index.js.map +1 -0
- package/dist/esm/src/json-rpc-socket.d.ts +39 -0
- package/dist/esm/src/json-rpc-socket.d.ts.map +1 -0
- package/dist/esm/src/json-rpc-socket.js +125 -0
- package/dist/esm/src/json-rpc-socket.js.map +1 -0
- package/dist/esm/src/lib/http-server-shutdown-handler.d.ts +10 -0
- package/dist/esm/src/lib/http-server-shutdown-handler.d.ts.map +1 -0
- package/dist/esm/src/lib/http-server-shutdown-handler.js +65 -0
- package/dist/esm/src/lib/http-server-shutdown-handler.js.map +1 -0
- package/dist/esm/src/lib/json-rpc-router.d.ts +30 -0
- package/dist/esm/src/lib/json-rpc-router.d.ts.map +1 -0
- package/dist/esm/src/lib/json-rpc-router.js +14 -0
- package/dist/esm/src/lib/json-rpc-router.js.map +1 -0
- package/dist/esm/src/lib/json-rpc.d.ts +54 -0
- package/dist/esm/src/lib/json-rpc.d.ts.map +1 -0
- package/dist/esm/src/lib/json-rpc.js +60 -0
- package/dist/esm/src/lib/json-rpc.js.map +1 -0
- package/dist/esm/src/main.d.ts +3 -0
- package/dist/esm/src/main.d.ts.map +1 -0
- package/dist/esm/src/main.js +11 -0
- package/dist/esm/src/main.js.map +1 -0
- package/dist/esm/src/metrics.d.ts +4 -0
- package/dist/esm/src/metrics.d.ts.map +1 -0
- package/dist/esm/src/metrics.js +13 -0
- package/dist/esm/src/metrics.js.map +1 -0
- package/dist/esm/src/plugin-loader.d.ts +10 -0
- package/dist/esm/src/plugin-loader.d.ts.map +1 -0
- package/dist/esm/src/plugin-loader.js +19 -0
- package/dist/esm/src/plugin-loader.js.map +1 -0
- package/dist/esm/src/process-handlers.d.ts +11 -0
- package/dist/esm/src/process-handlers.d.ts.map +1 -0
- package/dist/esm/src/process-handlers.js +40 -0
- package/dist/esm/src/process-handlers.js.map +1 -0
- package/dist/esm/src/registration/proof-of-work-manager.d.ts +93 -0
- package/dist/esm/src/registration/proof-of-work-manager.d.ts.map +1 -0
- package/dist/esm/src/registration/proof-of-work-manager.js +243 -0
- package/dist/esm/src/registration/proof-of-work-manager.js.map +1 -0
- package/dist/esm/src/registration/proof-of-work-types.d.ts +8 -0
- package/dist/esm/src/registration/proof-of-work-types.d.ts.map +1 -0
- package/dist/esm/src/registration/proof-of-work-types.js +2 -0
- package/dist/esm/src/registration/proof-of-work-types.js.map +1 -0
- package/dist/esm/src/registration/proof-of-work.d.ts +41 -0
- package/dist/esm/src/registration/proof-of-work.d.ts.map +1 -0
- package/dist/esm/src/registration/proof-of-work.js +69 -0
- package/dist/esm/src/registration/proof-of-work.js.map +1 -0
- package/dist/esm/src/registration/registration-manager.d.ts +55 -0
- package/dist/esm/src/registration/registration-manager.d.ts.map +1 -0
- package/dist/esm/src/registration/registration-manager.js +121 -0
- package/dist/esm/src/registration/registration-manager.js.map +1 -0
- package/dist/esm/src/registration/registration-store.d.ts +24 -0
- package/dist/esm/src/registration/registration-store.d.ts.map +1 -0
- package/dist/esm/src/registration/registration-store.js +56 -0
- package/dist/esm/src/registration/registration-store.js.map +1 -0
- package/dist/esm/src/registration/registration-types.d.ts +18 -0
- package/dist/esm/src/registration/registration-types.d.ts.map +1 -0
- package/dist/esm/src/registration/registration-types.js +2 -0
- package/dist/esm/src/registration/registration-types.js.map +1 -0
- package/dist/esm/src/storage.d.ts +24 -0
- package/dist/esm/src/storage.d.ts.map +1 -0
- package/dist/esm/src/storage.js +146 -0
- package/dist/esm/src/storage.js.map +1 -0
- package/dist/esm/src/web5-connect/sql-ttl-cache.d.ts +39 -0
- package/dist/esm/src/web5-connect/sql-ttl-cache.d.ts.map +1 -0
- package/dist/esm/src/web5-connect/sql-ttl-cache.js +106 -0
- package/dist/esm/src/web5-connect/sql-ttl-cache.js.map +1 -0
- package/dist/esm/src/web5-connect/web5-connect-server.d.ts +58 -0
- package/dist/esm/src/web5-connect/web5-connect-server.d.ts.map +1 -0
- package/dist/esm/src/web5-connect/web5-connect-server.js +77 -0
- package/dist/esm/src/web5-connect/web5-connect-server.js.map +1 -0
- package/dist/esm/src/ws-api.d.ts +13 -0
- package/dist/esm/src/ws-api.d.ts.map +1 -0
- package/dist/esm/src/ws-api.js +31 -0
- package/dist/esm/src/ws-api.js.map +1 -0
- package/dist/esm/tests/common-scenario-validator.d.ts +11 -0
- package/dist/esm/tests/common-scenario-validator.d.ts.map +1 -0
- package/dist/esm/tests/common-scenario-validator.js +114 -0
- package/dist/esm/tests/common-scenario-validator.js.map +1 -0
- package/dist/esm/tests/connection/connection-manager.spec.d.ts +2 -0
- package/dist/esm/tests/connection/connection-manager.spec.d.ts.map +1 -0
- package/dist/esm/tests/connection/connection-manager.spec.js +47 -0
- package/dist/esm/tests/connection/connection-manager.spec.js.map +1 -0
- package/dist/esm/tests/connection/socket-connection.spec.d.ts +2 -0
- package/dist/esm/tests/connection/socket-connection.spec.d.ts.map +1 -0
- package/dist/esm/tests/connection/socket-connection.spec.js +125 -0
- package/dist/esm/tests/connection/socket-connection.spec.js.map +1 -0
- package/dist/esm/tests/cors/http-api.browser.d.ts +2 -0
- package/dist/esm/tests/cors/http-api.browser.d.ts.map +1 -0
- package/dist/esm/tests/cors/http-api.browser.js +60 -0
- package/dist/esm/tests/cors/http-api.browser.js.map +1 -0
- package/dist/esm/tests/cors/ping.browser.d.ts +2 -0
- package/dist/esm/tests/cors/ping.browser.d.ts.map +1 -0
- package/dist/esm/tests/cors/ping.browser.js +7 -0
- package/dist/esm/tests/cors/ping.browser.js.map +1 -0
- package/dist/esm/tests/dwn-process-message.spec.d.ts +2 -0
- package/dist/esm/tests/dwn-process-message.spec.d.ts.map +1 -0
- package/dist/esm/tests/dwn-process-message.spec.js +172 -0
- package/dist/esm/tests/dwn-process-message.spec.js.map +1 -0
- package/dist/esm/tests/dwn-server.spec.d.ts +2 -0
- package/dist/esm/tests/dwn-server.spec.d.ts.map +1 -0
- package/dist/esm/tests/dwn-server.spec.js +49 -0
- package/dist/esm/tests/dwn-server.spec.js.map +1 -0
- package/dist/esm/tests/http-api.spec.d.ts +2 -0
- package/dist/esm/tests/http-api.spec.d.ts.map +1 -0
- package/dist/esm/tests/http-api.spec.js +775 -0
- package/dist/esm/tests/http-api.spec.js.map +1 -0
- package/dist/esm/tests/json-rpc-socket.spec.d.ts +2 -0
- package/dist/esm/tests/json-rpc-socket.spec.d.ts.map +1 -0
- package/dist/esm/tests/json-rpc-socket.spec.js +225 -0
- package/dist/esm/tests/json-rpc-socket.spec.js.map +1 -0
- package/dist/esm/tests/plugins/data-store-sqlite.d.ts +17 -0
- package/dist/esm/tests/plugins/data-store-sqlite.d.ts.map +1 -0
- package/dist/esm/tests/plugins/data-store-sqlite.js +23 -0
- package/dist/esm/tests/plugins/data-store-sqlite.js.map +1 -0
- package/dist/esm/tests/plugins/event-log-sqlite.d.ts +17 -0
- package/dist/esm/tests/plugins/event-log-sqlite.d.ts.map +1 -0
- package/dist/esm/tests/plugins/event-log-sqlite.js +23 -0
- package/dist/esm/tests/plugins/event-log-sqlite.js.map +1 -0
- package/dist/esm/tests/plugins/event-stream-in-memory.d.ts +17 -0
- package/dist/esm/tests/plugins/event-stream-in-memory.d.ts.map +1 -0
- package/dist/esm/tests/plugins/event-stream-in-memory.js +21 -0
- package/dist/esm/tests/plugins/event-stream-in-memory.js.map +1 -0
- package/dist/esm/tests/plugins/message-store-sqlite.d.ts +17 -0
- package/dist/esm/tests/plugins/message-store-sqlite.d.ts.map +1 -0
- package/dist/esm/tests/plugins/message-store-sqlite.js +23 -0
- package/dist/esm/tests/plugins/message-store-sqlite.js.map +1 -0
- package/dist/esm/tests/plugins/resumable-task-store-sqlite.d.ts +17 -0
- package/dist/esm/tests/plugins/resumable-task-store-sqlite.d.ts.map +1 -0
- package/dist/esm/tests/plugins/resumable-task-store-sqlite.js +23 -0
- package/dist/esm/tests/plugins/resumable-task-store-sqlite.js.map +1 -0
- package/dist/esm/tests/process-handler.spec.d.ts +2 -0
- package/dist/esm/tests/process-handler.spec.d.ts.map +1 -0
- package/dist/esm/tests/process-handler.spec.js +60 -0
- package/dist/esm/tests/process-handler.spec.js.map +1 -0
- package/dist/esm/tests/registration/proof-of-work-manager.spec.d.ts +2 -0
- package/dist/esm/tests/registration/proof-of-work-manager.spec.d.ts.map +1 -0
- package/dist/esm/tests/registration/proof-of-work-manager.spec.js +157 -0
- package/dist/esm/tests/registration/proof-of-work-manager.spec.js.map +1 -0
- package/dist/esm/tests/rpc-subscribe-close.spec.d.ts +2 -0
- package/dist/esm/tests/rpc-subscribe-close.spec.d.ts.map +1 -0
- package/dist/esm/tests/rpc-subscribe-close.spec.js +81 -0
- package/dist/esm/tests/rpc-subscribe-close.spec.js.map +1 -0
- package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.d.ts +2 -0
- package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.d.ts.map +1 -0
- package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.js +73 -0
- package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.js.map +1 -0
- package/dist/esm/tests/scenarios/registration.spec.d.ts +2 -0
- package/dist/esm/tests/scenarios/registration.spec.d.ts.map +1 -0
- package/dist/esm/tests/scenarios/registration.spec.js +507 -0
- package/dist/esm/tests/scenarios/registration.spec.js.map +1 -0
- package/dist/esm/tests/scenarios/web5-connect.spec.d.ts +2 -0
- package/dist/esm/tests/scenarios/web5-connect.spec.d.ts.map +1 -0
- package/dist/esm/tests/scenarios/web5-connect.spec.js +137 -0
- package/dist/esm/tests/scenarios/web5-connect.spec.js.map +1 -0
- package/dist/esm/tests/test-dwn.d.ts +7 -0
- package/dist/esm/tests/test-dwn.d.ts.map +1 -0
- package/dist/esm/tests/test-dwn.js +34 -0
- package/dist/esm/tests/test-dwn.js.map +1 -0
- package/dist/esm/tests/utils.d.ts +46 -0
- package/dist/esm/tests/utils.d.ts.map +1 -0
- package/dist/esm/tests/utils.js +116 -0
- package/dist/esm/tests/utils.js.map +1 -0
- package/dist/esm/tests/ws-api.spec.d.ts +2 -0
- package/dist/esm/tests/ws-api.spec.d.ts.map +1 -0
- package/dist/esm/tests/ws-api.spec.js +327 -0
- package/dist/esm/tests/ws-api.spec.js.map +1 -0
- package/package.json +119 -0
- package/src/config.ts +71 -0
- package/src/connection/connection-manager.ts +39 -0
- package/src/connection/socket-connection.ts +221 -0
- package/src/dwn-error.ts +38 -0
- package/src/dwn-server.ts +178 -0
- package/src/http-api.ts +541 -0
- package/src/index.ts +6 -0
- package/src/json-rpc-api.ts +11 -0
- package/src/json-rpc-handlers/dwn/index.ts +1 -0
- package/src/json-rpc-handlers/dwn/process-message.ts +123 -0
- package/src/json-rpc-handlers/subscription/close.ts +59 -0
- package/src/json-rpc-handlers/subscription/index.ts +1 -0
- package/src/json-rpc-socket.ts +155 -0
- package/src/lib/http-server-shutdown-handler.ts +79 -0
- package/src/lib/json-rpc-router.ts +52 -0
- package/src/lib/json-rpc.ts +126 -0
- package/src/main.ts +14 -0
- package/src/metrics.ts +14 -0
- package/src/plugin-loader.ts +17 -0
- package/src/process-handlers.ts +65 -0
- package/src/registration/proof-of-work-manager.ts +317 -0
- package/src/registration/proof-of-work-types.ts +7 -0
- package/src/registration/proof-of-work.ts +100 -0
- package/src/registration/registration-manager.ts +153 -0
- package/src/registration/registration-store.ts +79 -0
- package/src/registration/registration-types.ts +18 -0
- package/src/storage.ts +213 -0
- package/src/web5-connect/sql-ttl-cache.ts +137 -0
- package/src/web5-connect/web5-connect-server.ts +122 -0
- package/src/ws-api.ts +45 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import log from 'loglevel';
|
|
2
|
+
import prefix from 'loglevel-plugin-prefix';
|
|
3
|
+
import { config as defaultConfig } from './config.js';
|
|
4
|
+
import { getDwnConfig } from './storage.js';
|
|
5
|
+
import { HttpServerShutdownHandler } from './lib/http-server-shutdown-handler.js';
|
|
6
|
+
import { HttpApi } from './http-api.js';
|
|
7
|
+
import { PluginLoader } from './plugin-loader.js';
|
|
8
|
+
import { RegistrationManager } from './registration/registration-manager.js';
|
|
9
|
+
import { WsApi } from './ws-api.js';
|
|
10
|
+
import { Dwn, EventEmitterStream } from '@enbox/dwn-sdk-js';
|
|
11
|
+
import { removeProcessHandlers, setProcessHandlers } from './process-handlers.js';
|
|
12
|
+
/**
|
|
13
|
+
* State of the DwnServer, either Stopped or Started, to help short-circuit start and stop logic.
|
|
14
|
+
*/
|
|
15
|
+
var DwnServerState;
|
|
16
|
+
(function (DwnServerState) {
|
|
17
|
+
DwnServerState[DwnServerState["Stopped"] = 0] = "Stopped";
|
|
18
|
+
DwnServerState[DwnServerState["Started"] = 1] = "Started";
|
|
19
|
+
})(DwnServerState || (DwnServerState = {}));
|
|
20
|
+
export class DwnServer {
|
|
21
|
+
serverState = DwnServerState.Stopped;
|
|
22
|
+
processHandlers;
|
|
23
|
+
/**
|
|
24
|
+
* A custom DID resolver to use in the DWN.
|
|
25
|
+
* Mainly for testing purposes. Ignored if `dwn` is provided.
|
|
26
|
+
*/
|
|
27
|
+
didResolver;
|
|
28
|
+
dwn;
|
|
29
|
+
config;
|
|
30
|
+
#httpServerShutdownHandler;
|
|
31
|
+
#httpApi;
|
|
32
|
+
#wsApi;
|
|
33
|
+
/**
|
|
34
|
+
* @param options.dwn - Dwn instance to use as an override. Registration endpoint will not be enabled if this is provided.
|
|
35
|
+
*/
|
|
36
|
+
constructor(options = {}) {
|
|
37
|
+
this.config = options.config ?? defaultConfig;
|
|
38
|
+
this.didResolver = options.didResolver;
|
|
39
|
+
this.dwn = options.dwn;
|
|
40
|
+
log.setLevel(this.config.logLevel);
|
|
41
|
+
prefix.reg(log);
|
|
42
|
+
prefix.apply(log);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Starts the DWN server.
|
|
46
|
+
*/
|
|
47
|
+
async start() {
|
|
48
|
+
if (this.serverState === DwnServerState.Started) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
await this.#setupServer();
|
|
52
|
+
this.processHandlers = setProcessHandlers(this);
|
|
53
|
+
this.serverState = DwnServerState.Started;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Function to setup the servers (HTTP and WebSocket)
|
|
57
|
+
* The DWN creation is secondary and only happens if it hasn't already been done.
|
|
58
|
+
*/
|
|
59
|
+
async #setupServer() {
|
|
60
|
+
let registrationManager;
|
|
61
|
+
if (!this.dwn) {
|
|
62
|
+
// undefined registrationStoreUrl is used as a signal that there is no need for tenant registration, DWN is open for all.
|
|
63
|
+
registrationManager = await RegistrationManager.create({
|
|
64
|
+
registrationStoreUrl: this.config.registrationStoreUrl,
|
|
65
|
+
termsOfServiceFilePath: this.config.termsOfServiceFilePath,
|
|
66
|
+
proofOfWorkChallengeNonceSeed: this.config.registrationProofOfWorkSeed,
|
|
67
|
+
proofOfWorkInitialMaximumAllowedHash: this.config.registrationProofOfWorkInitialMaxHash,
|
|
68
|
+
});
|
|
69
|
+
let eventStream;
|
|
70
|
+
if (this.config.webSocketSupport) {
|
|
71
|
+
// If Even Stream plugin is not specified, use `EventEmitterStream` implementation as default.
|
|
72
|
+
if (this.config.eventStreamPluginPath === undefined || this.config.eventStreamPluginPath === '') {
|
|
73
|
+
eventStream = new EventEmitterStream();
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
eventStream = await PluginLoader.loadPlugin(this.config.eventStreamPluginPath);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const dwnConfig = await getDwnConfig(this.config, {
|
|
80
|
+
didResolver: this.didResolver,
|
|
81
|
+
tenantGate: registrationManager,
|
|
82
|
+
eventStream,
|
|
83
|
+
});
|
|
84
|
+
this.dwn = await Dwn.create(dwnConfig);
|
|
85
|
+
}
|
|
86
|
+
this.#httpApi = await HttpApi.create(this.config, this.dwn, registrationManager);
|
|
87
|
+
await this.#httpApi.start(this.config.port);
|
|
88
|
+
log.info(`HttpServer listening on port ${this.config.port}`);
|
|
89
|
+
this.#httpServerShutdownHandler = new HttpServerShutdownHandler(this.#httpApi.server);
|
|
90
|
+
if (this.config.webSocketSupport) {
|
|
91
|
+
this.#wsApi = new WsApi(this.#httpApi.server, this.dwn);
|
|
92
|
+
this.#wsApi.start();
|
|
93
|
+
log.info('WebSocketServer ready...');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Stops the DWN server.
|
|
98
|
+
*/
|
|
99
|
+
async stop() {
|
|
100
|
+
if (this.serverState === DwnServerState.Stopped) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
await this.dwn.close();
|
|
104
|
+
await this.#httpApi.close();
|
|
105
|
+
// close WebSocket server if it was initialized
|
|
106
|
+
if (this.#wsApi !== undefined) {
|
|
107
|
+
await this.#wsApi.close();
|
|
108
|
+
}
|
|
109
|
+
await new Promise((resolve) => {
|
|
110
|
+
this.#httpServerShutdownHandler.stop(() => {
|
|
111
|
+
resolve();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
removeProcessHandlers(this.processHandlers);
|
|
115
|
+
this.serverState = DwnServerState.Stopped;
|
|
116
|
+
}
|
|
117
|
+
get httpServer() {
|
|
118
|
+
return this.#httpApi.server;
|
|
119
|
+
}
|
|
120
|
+
get wsServer() {
|
|
121
|
+
return this.#wsApi?.server;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Gets the RegistrationManager for testing purposes.
|
|
125
|
+
*/
|
|
126
|
+
get registrationManager() {
|
|
127
|
+
return this.#httpApi.registrationManager;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=dwn-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dwn-server.js","sourceRoot":"","sources":["../../../src/dwn-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Dwn } from '@enbox/dwn-sdk-js';
|
|
2
|
+
import type { Express } from 'express';
|
|
3
|
+
import http from 'http';
|
|
4
|
+
import type { DwnServerConfig } from './config.js';
|
|
5
|
+
import type { RegistrationManager } from './registration/registration-manager.js';
|
|
6
|
+
import { Web5ConnectServer } from './web5-connect/web5-connect-server.js';
|
|
7
|
+
export declare class HttpApi {
|
|
8
|
+
#private;
|
|
9
|
+
web5ConnectServer: Web5ConnectServer;
|
|
10
|
+
registrationManager: RegistrationManager;
|
|
11
|
+
dwn: Dwn;
|
|
12
|
+
private constructor();
|
|
13
|
+
static create(config: DwnServerConfig, dwn: Dwn, registrationManager?: RegistrationManager): Promise<HttpApi>;
|
|
14
|
+
get server(): http.Server;
|
|
15
|
+
get api(): Express;
|
|
16
|
+
/**
|
|
17
|
+
* Starts the HTTP API endpoint on the given port.
|
|
18
|
+
* @returns The HTTP server instance.
|
|
19
|
+
*/
|
|
20
|
+
start(port: number): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Stops the HTTP API endpoint.
|
|
23
|
+
*/
|
|
24
|
+
close(): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=http-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-api.d.ts","sourceRoot":"","sources":["../../../src/http-api.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,GAAG,EAAuD,MAAM,mBAAmB,CAAC;AAGlG,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAG1D,OAAO,IAAI,MAAM,MAAM,CAAC;AASxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAGlF,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAM1E,qBAAa,OAAO;;IAKlB,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,GAAG,EAAE,GAAG,CAAC;IAET,OAAO;WAEa,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,EAAE,GAAG,EAAE,mBAAmB,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC;IAuC1H,IAAI,MAAM,IAAI,IAAI,CAAC,MAAM,CAExB;IAED,IAAI,GAAG,IAAI,OAAO,CAEjB;IA2aD;;;OAGG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASxC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAc7B"}
|
|
@@ -0,0 +1,474 @@
|
|
|
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
|
+
import log from 'loglevel';
|
|
7
|
+
import { register } from 'prom-client';
|
|
8
|
+
import responseTime from 'response-time';
|
|
9
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
10
|
+
import { config } from './config.js';
|
|
11
|
+
import { jsonRpcRouter } from './json-rpc-api.js';
|
|
12
|
+
import { Web5ConnectServer } from './web5-connect/web5-connect-server.js';
|
|
13
|
+
import { createJsonRpcErrorResponse, JsonRpcErrorCodes } from './lib/json-rpc.js';
|
|
14
|
+
import { requestCounter, responseHistogram } from './metrics.js';
|
|
15
|
+
import { Convert } from '@enbox/common';
|
|
16
|
+
export class HttpApi {
|
|
17
|
+
#config;
|
|
18
|
+
#packageInfo;
|
|
19
|
+
#api;
|
|
20
|
+
#server;
|
|
21
|
+
web5ConnectServer;
|
|
22
|
+
registrationManager;
|
|
23
|
+
dwn;
|
|
24
|
+
constructor() { }
|
|
25
|
+
static async create(config, dwn, registrationManager) {
|
|
26
|
+
const httpApi = new HttpApi();
|
|
27
|
+
log.info(config);
|
|
28
|
+
httpApi.#packageInfo = {
|
|
29
|
+
server: config.serverName,
|
|
30
|
+
};
|
|
31
|
+
try {
|
|
32
|
+
// We populate the `version` and `sdkVersion` properties from the `package.json` file.
|
|
33
|
+
const packageJson = JSON.parse(readFileSync(config.packageJsonPath).toString());
|
|
34
|
+
httpApi.#packageInfo.version = packageJson.version;
|
|
35
|
+
httpApi.#packageInfo.sdkVersion = packageJson.dependencies ? packageJson.dependencies['@enbox/dwn-sdk-js'] : undefined;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
log.info('could not read `package.json` for version info', error);
|
|
39
|
+
}
|
|
40
|
+
httpApi.#config = config;
|
|
41
|
+
httpApi.#api = express();
|
|
42
|
+
httpApi.#server = http.createServer(httpApi.#api);
|
|
43
|
+
httpApi.dwn = dwn;
|
|
44
|
+
if (registrationManager !== undefined) {
|
|
45
|
+
httpApi.registrationManager = registrationManager;
|
|
46
|
+
}
|
|
47
|
+
// create the Web5 Connect Server
|
|
48
|
+
httpApi.web5ConnectServer = await Web5ConnectServer.create({
|
|
49
|
+
baseUrl: config.baseUrl,
|
|
50
|
+
sqlTtlCacheUrl: config.ttlCacheUrl,
|
|
51
|
+
});
|
|
52
|
+
httpApi.#setupMiddleware();
|
|
53
|
+
httpApi.#setupRoutes();
|
|
54
|
+
return httpApi;
|
|
55
|
+
}
|
|
56
|
+
get server() {
|
|
57
|
+
return this.#server;
|
|
58
|
+
}
|
|
59
|
+
get api() {
|
|
60
|
+
return this.#api;
|
|
61
|
+
}
|
|
62
|
+
#setupMiddleware() {
|
|
63
|
+
this.#api.use(cors({ exposedHeaders: 'dwn-response' }));
|
|
64
|
+
this.#api.use(express.json());
|
|
65
|
+
// We enable the formData middleware to handle multipart/form-data requests.
|
|
66
|
+
// This is necessary for the endpoints used by the Web5 Connect Server/OIDC flow.
|
|
67
|
+
this.#api.use(express.urlencoded({ extended: true }));
|
|
68
|
+
this.#api.use(responseTime((req, res, time) => {
|
|
69
|
+
const url = req.url === '/' ? '/jsonrpc' : req.url;
|
|
70
|
+
const route = (req.method + url)
|
|
71
|
+
.toLowerCase()
|
|
72
|
+
.replace(/[:.]/g, '')
|
|
73
|
+
.replace(/\//g, '_');
|
|
74
|
+
const statusCode = res.statusCode.toString();
|
|
75
|
+
responseHistogram.labels(route, statusCode).observe(time);
|
|
76
|
+
log.info(req.method, decodeURI(req.url), res.statusCode);
|
|
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);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
return res.sendStatus(400);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else if (reply.status.code === 401) {
|
|
97
|
+
return res.sendStatus(404);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
return res.status(reply.status.code).send(reply);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
this.#api.get('/health', (_req, res) => {
|
|
104
|
+
// return 200 ok
|
|
105
|
+
return res.json({ ok: true });
|
|
106
|
+
});
|
|
107
|
+
this.#api.get('/metrics', async (req, res) => {
|
|
108
|
+
try {
|
|
109
|
+
res.set('Content-Type', register.contentType);
|
|
110
|
+
res.end(await register.metrics());
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
res.status(500).end(e);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
// Returns the data for the most recently published record under a given protocol path collection, if one is present
|
|
117
|
+
this.#api.get('/:did/read/protocols/:protocol/*', async (req, res) => {
|
|
118
|
+
if (!req.params[0]) {
|
|
119
|
+
return res.status(400).send('protocol path is required');
|
|
120
|
+
}
|
|
121
|
+
// wrap request in a try-catch block to handle any unexpected errors
|
|
122
|
+
try {
|
|
123
|
+
const queryOptions = { filter: {} };
|
|
124
|
+
for (const param in req.query) {
|
|
125
|
+
const keys = param.split('.');
|
|
126
|
+
const lastKey = keys.pop();
|
|
127
|
+
const lastLevelObject = keys.reduce((obj, key) => obj[key] = obj[key] || {}, queryOptions);
|
|
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
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
log.error(`Error processing request: ${decodeURI(req.url)}`, error);
|
|
162
|
+
return res.sendStatus(400);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
this.#api.get('/:did/read/protocols/:protocol', async (req, res) => {
|
|
166
|
+
// wrap request in a try-catch block to handle any unexpected errors
|
|
167
|
+
try {
|
|
168
|
+
// the protocol segment is base64url encoded, as the actual protocol is a URL
|
|
169
|
+
// we decode it here in order to filter for the correct protocol
|
|
170
|
+
const protocol = Convert.base64Url(req.params.protocol).toString();
|
|
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
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
log.error(`Error processing request: ${decodeURI(req.url)}`, error);
|
|
193
|
+
return res.sendStatus(400);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
const recordsReadHandler = async (req, res) => {
|
|
197
|
+
const record = await RecordsRead.create({
|
|
198
|
+
filter: { recordId: req.params.id },
|
|
199
|
+
});
|
|
200
|
+
const reply = await this.dwn.processMessage(req.params.did, record.message);
|
|
201
|
+
return readReplyHandler(res, reply);
|
|
202
|
+
};
|
|
203
|
+
this.#api.get('/:did/read/records/:id', recordsReadHandler);
|
|
204
|
+
this.#api.get('/:did/records/:id', recordsReadHandler);
|
|
205
|
+
this.#api.get('/:did/query/protocols', async (req, res) => {
|
|
206
|
+
const query = await ProtocolsQuery.create({});
|
|
207
|
+
const { entries, status } = await this.dwn.processMessage(req.params.did, query.message);
|
|
208
|
+
if (status.code === 200) {
|
|
209
|
+
res.status(status.code);
|
|
210
|
+
res.json(entries);
|
|
211
|
+
}
|
|
212
|
+
else if (status.code === 401) {
|
|
213
|
+
return res.sendStatus(404);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
return res.sendStatus(status.code);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
this.#api.get('/:did/query', async (req, res) => {
|
|
220
|
+
try {
|
|
221
|
+
// builds a nested object from flat keys with dot notation which may share the same parent path
|
|
222
|
+
// e.g. "did:dht:123/query?filter.protocol=foo&filter.protocolPath=bar" becomes
|
|
223
|
+
// {
|
|
224
|
+
// filter: {
|
|
225
|
+
// protocol: 'foo',
|
|
226
|
+
// protocolPath: 'bar'
|
|
227
|
+
// }
|
|
228
|
+
// }
|
|
229
|
+
const recordsQueryOptions = {};
|
|
230
|
+
for (const param in req.query) {
|
|
231
|
+
const keys = param.split('.');
|
|
232
|
+
const lastKey = keys.pop();
|
|
233
|
+
const lastLevelObject = keys.reduce((obj, key) => obj[key] = obj[key] || {}, recordsQueryOptions);
|
|
234
|
+
lastLevelObject[lastKey] = req.query[param];
|
|
235
|
+
}
|
|
236
|
+
const recordsQuery = await RecordsQuery.create({
|
|
237
|
+
filter: recordsQueryOptions.filter,
|
|
238
|
+
pagination: recordsQueryOptions.pagination,
|
|
239
|
+
dateSort: recordsQueryOptions.dateSort,
|
|
240
|
+
});
|
|
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
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
// error should only occur when we are unable to create the RecordsQuery message internally, making it a client error
|
|
248
|
+
return res.status(400).send(error);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
this.#api.get('/', (_req, res) => {
|
|
252
|
+
// return a plain text string
|
|
253
|
+
res.setHeader('content-type', 'text/plain');
|
|
254
|
+
return res.send('please use a web5 client, for example: https://github.com/TBD54566975/web5-js ');
|
|
255
|
+
});
|
|
256
|
+
this.#api.post('/', async (req, res) => {
|
|
257
|
+
const dwnRpcRequestString = req.headers['dwn-request'];
|
|
258
|
+
if (!dwnRpcRequestString) {
|
|
259
|
+
const reply = createJsonRpcErrorResponse(uuidv4(), JsonRpcErrorCodes.BadRequest, 'request payload required.');
|
|
260
|
+
return res.status(400).json(reply);
|
|
261
|
+
}
|
|
262
|
+
let dwnRpcRequest;
|
|
263
|
+
try {
|
|
264
|
+
dwnRpcRequest = JSON.parse(dwnRpcRequestString);
|
|
265
|
+
}
|
|
266
|
+
catch (e) {
|
|
267
|
+
const reply = createJsonRpcErrorResponse(uuidv4(), JsonRpcErrorCodes.BadRequest, e.message);
|
|
268
|
+
return res.status(400).json(reply);
|
|
269
|
+
}
|
|
270
|
+
// Check whether data was provided in the request body
|
|
271
|
+
const contentLength = req.headers['content-length'];
|
|
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);
|
|
285
|
+
}
|
|
286
|
+
requestCounter.inc({
|
|
287
|
+
method: dwnRpcRequest.method,
|
|
288
|
+
status: jsonRpcResponse?.result?.reply?.status?.code || 0,
|
|
289
|
+
});
|
|
290
|
+
if (responseDataStream) {
|
|
291
|
+
res.setHeader('content-type', 'application/octet-stream');
|
|
292
|
+
res.setHeader('dwn-response', JSON.stringify(jsonRpcResponse));
|
|
293
|
+
return responseDataStream.pipe(res);
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
return res.json(jsonRpcResponse);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
this.#setupRegistrationRoutes();
|
|
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');
|
|
305
|
+
}
|
|
306
|
+
if (config.termsOfServiceFilePath !== undefined) {
|
|
307
|
+
registrationRequirements.push('terms-of-service');
|
|
308
|
+
}
|
|
309
|
+
res.json({
|
|
310
|
+
url: config.baseUrl,
|
|
311
|
+
server: this.#packageInfo.server,
|
|
312
|
+
maxFileSize: config.maxRecordDataSize,
|
|
313
|
+
registrationRequirements: registrationRequirements,
|
|
314
|
+
version: this.#packageInfo.version,
|
|
315
|
+
sdkVersion: this.#packageInfo.sdkVersion,
|
|
316
|
+
webSocketSupport: config.webSocketSupport,
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
this.#setupWeb5ConnectServerRoutes();
|
|
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);
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
if (this.#config.termsOfServiceFilePath !== undefined) {
|
|
329
|
+
this.#api.get('/registration/terms-of-service', (_req, res) => res.send(this.registrationManager.getTermsOfService()));
|
|
330
|
+
}
|
|
331
|
+
if (this.#config.registrationStoreUrl !== undefined) {
|
|
332
|
+
this.#api.post('/registration', async (req, res) => {
|
|
333
|
+
const requestBody = req.body;
|
|
334
|
+
log.info('Registration request:', requestBody);
|
|
335
|
+
try {
|
|
336
|
+
await this.registrationManager.handleRegistrationRequest(requestBody);
|
|
337
|
+
res.status(200).json({ success: true });
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
const dwnServerError = error;
|
|
341
|
+
if (dwnServerError.code !== undefined) {
|
|
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
|
+
}
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
#setupWeb5ConnectServerRoutes() {
|
|
353
|
+
/**
|
|
354
|
+
* Endpoint allows a Client app (RP) to submit an Authorization Request.
|
|
355
|
+
* The Authorization Request is stored on the server, and a unique `request_uri` is returned to the Client app.
|
|
356
|
+
* The Client app can then provide this `request_uri` to the Provider app (wallet).
|
|
357
|
+
* The Provider app uses the `request_uri` to retrieve the stored Authorization Request.
|
|
358
|
+
*/
|
|
359
|
+
this.#api.post('/connect/par', async (req, res) => {
|
|
360
|
+
log.info('Storing Pushed Authorization Request (PAR) request...');
|
|
361
|
+
// TODO: Add validation for request too large HTTP 413: https://github.com/TBD54566975/dwn-server/issues/146
|
|
362
|
+
// TODO: Add validation for too many requests HTTP 429: https://github.com/TBD54566975/dwn-server/issues/147
|
|
363
|
+
if (!req.body.request) {
|
|
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({
|
|
375
|
+
ok: false,
|
|
376
|
+
status: {
|
|
377
|
+
code: 400,
|
|
378
|
+
message: "Bad Request: 'request_uri' parameter is not allowed in PAR",
|
|
379
|
+
},
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
const result = await this.web5ConnectServer.setWeb5ConnectRequest(req.body.request);
|
|
383
|
+
res.status(201).json(result);
|
|
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({
|
|
394
|
+
ok: false,
|
|
395
|
+
status: { code: 404, message: 'Not Found' }
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
res.set('Content-Type', 'application/jwt');
|
|
400
|
+
res.send(requestObjectJwt);
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
/**
|
|
404
|
+
* Endpoint that the Provider sends the Authorization Response to
|
|
405
|
+
*/
|
|
406
|
+
this.#api.post('/connect/callback', async (req, res) => {
|
|
407
|
+
log.info('Storing Identity Provider (wallet) pushed response with ID token...');
|
|
408
|
+
// Store the ID token.
|
|
409
|
+
const idToken = req.body.id_token;
|
|
410
|
+
const state = req.body.state;
|
|
411
|
+
if (idToken !== undefined && state != undefined) {
|
|
412
|
+
await this.web5ConnectServer.setWeb5ConnectResponse(state, idToken);
|
|
413
|
+
res.status(201).json({
|
|
414
|
+
ok: true,
|
|
415
|
+
status: { code: 201, message: 'Created' }
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
res.status(400).json({
|
|
420
|
+
ok: false,
|
|
421
|
+
status: { code: 400, message: 'Bad Request' }
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
/**
|
|
426
|
+
* Endpoint for the connecting Client to retrieve the Authorization Response
|
|
427
|
+
*/
|
|
428
|
+
this.#api.get('/connect/token/:state.jwt', async (req, res) => {
|
|
429
|
+
log.info(`Retrieving ID token for state: ${req.params.state}...`);
|
|
430
|
+
// Look up the ID token.
|
|
431
|
+
const idToken = await this.web5ConnectServer.getWeb5ConnectResponse(req.params.state);
|
|
432
|
+
if (!idToken) {
|
|
433
|
+
res.status(404).json({
|
|
434
|
+
ok: false,
|
|
435
|
+
status: { code: 404, message: 'Not Found' }
|
|
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);
|
|
465
|
+
}
|
|
466
|
+
else {
|
|
467
|
+
resolve();
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
this.server.closeAllConnections();
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
//# sourceMappingURL=http-api.js.map
|