jinaga-server 3.1.0 → 3.1.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/dist/http/line-reader.d.ts +2 -0
- package/dist/http/line-reader.js +68 -0
- package/dist/http/line-reader.js.map +1 -0
- package/dist/http/router.d.ts +4 -1
- package/dist/http/router.js +119 -15
- package/dist/http/router.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/jinaga-server.d.ts +1 -0
- package/dist/jinaga-server.js +1 -1
- package/dist/jinaga-server.js.map +1 -1
- package/http/sans-auth/Feeds options.http +1 -0
- package/http/sans-auth/Save graph.http +6 -0
- package/http/sans-auth/Save json.http +13 -0
- package/http/with-auth/Create post with auth.http +24 -0
- package/http/with-auth/Define authorization rules.http +25 -0
- package/http/with-auth/Define distribution rules.http +30 -0
- package/http/with-auth/Feed sites.http +3 -0
- package/http/with-auth/Feeds options.http +1 -0
- package/http/with-auth/Feeds sites.http +32 -0
- package/http/with-auth/Get posts with auth.http +32 -0
- package/http/with-auth/Load titles.http +21 -0
- package/integration-test/jinaga-test/Dockerfile +1 -1
- package/package.json +2 -2
- package/replicator/index.ts +0 -2
- package/replicator/package-lock.json +12 -43
- package/replicator/package.json +1 -3
- package/src/http/line-reader.ts +69 -0
- package/src/http/router.ts +147 -19
- package/src/index.ts +1 -0
- package/src/jinaga-server.ts +2 -1
- package/dist/http/serializer.d.ts +0 -11
- package/dist/http/serializer.js +0 -68
- package/dist/http/serializer.js.map +0 -1
- package/src/http/serializer.ts +0 -81
- package/test/http/serializerSpec.ts +0 -140
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
POST {{replicatorUrl}}/load
|
|
2
|
+
Authorization: oauth2 authorization_code
|
|
3
|
+
Content-Type: application/json
|
|
4
|
+
Accept: application/x-jinaga-graph-v1
|
|
5
|
+
|
|
6
|
+
{
|
|
7
|
+
"references": [
|
|
8
|
+
{
|
|
9
|
+
"type": "Blog.Post",
|
|
10
|
+
"hash": "XiDu5OWoNUwZhTBs+mwHrxjBDlFjBmDIgEt9ZXmCeMq8uv3Tv89Am6GQSt2ppl4wGG1UWMFxd9tstg5NMSSjKg=="
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"type": "Blog.Post.Title",
|
|
14
|
+
"hash": "n+hKtiGWHVu16UlxKY73Va+jsriQ/UxVFn3DCdXQEccWeXN5/SIxhWXmuMlfzelpHjuwPgCyeyid/jJrkJRRtQ=="
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"type": "Blog.Post.Title",
|
|
18
|
+
"hash": "iuRHB3Zkj0P3+b7XPvbEzA/ANdJ3C8Bm1dJ/nW8I9Tb+9VdHQP5g2bpp8hwS4KqzecgG6KSG6VCNOY7RoaEsIQ=="
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jinaga-server",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "Data management for web and mobile applications.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"express": "^4.19.2",
|
|
51
|
-
"jinaga": "^6.
|
|
51
|
+
"jinaga": "^6.1.1",
|
|
52
52
|
"node-forge": "^1.3.1",
|
|
53
53
|
"pg": "^8.11.3"
|
|
54
54
|
}
|
package/replicator/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import cors = require("cors");
|
|
2
1
|
import express = require("express");
|
|
3
2
|
import * as http from "http";
|
|
4
3
|
import { JinagaServer } from "../src";
|
|
@@ -15,7 +14,6 @@ const server = http.createServer(app);
|
|
|
15
14
|
app.set('port', process.env.PORT || 8080);
|
|
16
15
|
app.use(express.json());
|
|
17
16
|
app.use(express.text());
|
|
18
|
-
app.use(cors());
|
|
19
17
|
|
|
20
18
|
const pgConnection = process.env.JINAGA_POSTGRESQL ||
|
|
21
19
|
'postgresql://appuser:apppw@localhost:5432/appdb';
|
|
@@ -9,12 +9,10 @@
|
|
|
9
9
|
"version": "1.0.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"
|
|
13
|
-
"express": "^4.18.3",
|
|
12
|
+
"express": "^4.19.2",
|
|
14
13
|
"pg": "8.11.3"
|
|
15
14
|
},
|
|
16
15
|
"devDependencies": {
|
|
17
|
-
"@types/cors": "^2.8.12",
|
|
18
16
|
"@types/express": "^4.17.13",
|
|
19
17
|
"typescript": "^4.7.4"
|
|
20
18
|
}
|
|
@@ -38,23 +36,14 @@
|
|
|
38
36
|
"@types/node": "*"
|
|
39
37
|
}
|
|
40
38
|
},
|
|
41
|
-
"node_modules/@types/cors": {
|
|
42
|
-
"version": "2.8.13",
|
|
43
|
-
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz",
|
|
44
|
-
"integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==",
|
|
45
|
-
"dev": true,
|
|
46
|
-
"dependencies": {
|
|
47
|
-
"@types/node": "*"
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
39
|
"node_modules/@types/express": {
|
|
51
|
-
"version": "4.17.
|
|
52
|
-
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.
|
|
53
|
-
"integrity": "sha512-
|
|
40
|
+
"version": "4.17.21",
|
|
41
|
+
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
|
|
42
|
+
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
|
|
54
43
|
"dev": true,
|
|
55
44
|
"dependencies": {
|
|
56
45
|
"@types/body-parser": "*",
|
|
57
|
-
"@types/express-serve-static-core": "^4.17.
|
|
46
|
+
"@types/express-serve-static-core": "^4.17.33",
|
|
58
47
|
"@types/qs": "*",
|
|
59
48
|
"@types/serve-static": "*"
|
|
60
49
|
}
|
|
@@ -198,9 +187,9 @@
|
|
|
198
187
|
}
|
|
199
188
|
},
|
|
200
189
|
"node_modules/cookie": {
|
|
201
|
-
"version": "0.
|
|
202
|
-
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.
|
|
203
|
-
"integrity": "sha512-
|
|
190
|
+
"version": "0.6.0",
|
|
191
|
+
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
|
192
|
+
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
|
204
193
|
"engines": {
|
|
205
194
|
"node": ">= 0.6"
|
|
206
195
|
}
|
|
@@ -210,18 +199,6 @@
|
|
|
210
199
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
|
211
200
|
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
|
|
212
201
|
},
|
|
213
|
-
"node_modules/cors": {
|
|
214
|
-
"version": "2.8.5",
|
|
215
|
-
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
|
216
|
-
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
|
217
|
-
"dependencies": {
|
|
218
|
-
"object-assign": "^4",
|
|
219
|
-
"vary": "^1"
|
|
220
|
-
},
|
|
221
|
-
"engines": {
|
|
222
|
-
"node": ">= 0.10"
|
|
223
|
-
}
|
|
224
|
-
},
|
|
225
202
|
"node_modules/debug": {
|
|
226
203
|
"version": "2.6.9",
|
|
227
204
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
|
@@ -309,16 +286,16 @@
|
|
|
309
286
|
}
|
|
310
287
|
},
|
|
311
288
|
"node_modules/express": {
|
|
312
|
-
"version": "4.
|
|
313
|
-
"resolved": "https://registry.npmjs.org/express/-/express-4.
|
|
314
|
-
"integrity": "sha512-
|
|
289
|
+
"version": "4.19.2",
|
|
290
|
+
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
|
|
291
|
+
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
|
|
315
292
|
"dependencies": {
|
|
316
293
|
"accepts": "~1.3.8",
|
|
317
294
|
"array-flatten": "1.1.1",
|
|
318
295
|
"body-parser": "1.20.2",
|
|
319
296
|
"content-disposition": "0.5.4",
|
|
320
297
|
"content-type": "~1.0.4",
|
|
321
|
-
"cookie": "0.
|
|
298
|
+
"cookie": "0.6.0",
|
|
322
299
|
"cookie-signature": "1.0.6",
|
|
323
300
|
"debug": "2.6.9",
|
|
324
301
|
"depd": "2.0.0",
|
|
@@ -566,14 +543,6 @@
|
|
|
566
543
|
"node": ">= 0.6"
|
|
567
544
|
}
|
|
568
545
|
},
|
|
569
|
-
"node_modules/object-assign": {
|
|
570
|
-
"version": "4.1.1",
|
|
571
|
-
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
|
572
|
-
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
|
573
|
-
"engines": {
|
|
574
|
-
"node": ">=0.10.0"
|
|
575
|
-
}
|
|
576
|
-
},
|
|
577
546
|
"node_modules/object-inspect": {
|
|
578
547
|
"version": "1.13.1",
|
|
579
548
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
|
package/replicator/package.json
CHANGED
|
@@ -15,12 +15,10 @@
|
|
|
15
15
|
"author": "Michael L Perry",
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"
|
|
19
|
-
"express": "^4.18.3",
|
|
18
|
+
"express": "^4.19.2",
|
|
20
19
|
"pg": "8.11.3"
|
|
21
20
|
},
|
|
22
21
|
"devDependencies": {
|
|
23
|
-
"@types/cors": "^2.8.12",
|
|
24
22
|
"@types/express": "^4.17.13",
|
|
25
23
|
"typescript": "^4.7.4"
|
|
26
24
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const PauseAt = 10;
|
|
2
|
+
const ResumeAt = 5;
|
|
3
|
+
|
|
4
|
+
export function createLineReader(stream: NodeJS.ReadableStream): () => Promise<string | null> {
|
|
5
|
+
// Store the bytes read so far that have not yet been processed.
|
|
6
|
+
let buffer = Buffer.alloc(0);
|
|
7
|
+
// Store a queue of promises that resolve to the next line read.
|
|
8
|
+
let promiseQueue: Promise<string | null>[] = [];
|
|
9
|
+
// Initialize the queue.
|
|
10
|
+
let resolve: (value: string | null) => void;
|
|
11
|
+
let reject: (reason: any) => void;
|
|
12
|
+
promiseQueue.push(new Promise<string | null>((res, rej) => { resolve = res; reject = rej; }));
|
|
13
|
+
|
|
14
|
+
stream.on('data', (data: Buffer) => {
|
|
15
|
+
let newline = -1;
|
|
16
|
+
// Look for the first newline in the data.
|
|
17
|
+
while ((newline = data.indexOf(10)) >= 0) {
|
|
18
|
+
// Extract the line from the data.
|
|
19
|
+
let line = Buffer.concat([buffer, data.slice(0, newline)]).toString();
|
|
20
|
+
data = data.slice(newline + 1);
|
|
21
|
+
buffer = Buffer.alloc(0);
|
|
22
|
+
if (line.charCodeAt(0) === 0xFEFF) {
|
|
23
|
+
// Remove BOM
|
|
24
|
+
line = line.slice(1);
|
|
25
|
+
}
|
|
26
|
+
// Resolve the promise with the line.
|
|
27
|
+
resolve(line);
|
|
28
|
+
// Create a new promise for the next line.
|
|
29
|
+
promiseQueue.push(new Promise<string | null>((res, rej) => { resolve = res; reject = rej; }));
|
|
30
|
+
|
|
31
|
+
// Apply backpressure if the promise queue gets too deep
|
|
32
|
+
if (promiseQueue.length > PauseAt) {
|
|
33
|
+
stream.pause();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Append the remaining data to the buffer.
|
|
37
|
+
buffer = Buffer.concat([buffer, data]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
stream.on('end', () => {
|
|
41
|
+
// Extract the last line from the buffer.
|
|
42
|
+
let line = buffer.toString();
|
|
43
|
+
if (line.charCodeAt(0) === 0xFEFF) {
|
|
44
|
+
// Remove BOM
|
|
45
|
+
line = line.slice(1);
|
|
46
|
+
}
|
|
47
|
+
// If the stream ends with a newline, then don't add an empty line.
|
|
48
|
+
if (line.length > 0) {
|
|
49
|
+
resolve(line);
|
|
50
|
+
promiseQueue.push(new Promise<string | null>((res, rej) => { resolve = res; reject = rej; }));
|
|
51
|
+
}
|
|
52
|
+
resolve(null);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
stream.on('error', (e: Error) => {
|
|
56
|
+
reject(e);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return () => {
|
|
60
|
+
if (promiseQueue.length === 0) {
|
|
61
|
+
throw new Error("Line reader has been closed.");
|
|
62
|
+
}
|
|
63
|
+
const promise = promiseQueue.shift()!;
|
|
64
|
+
if (stream.isPaused() && promiseQueue.length <= ResumeAt) {
|
|
65
|
+
stream.resume();
|
|
66
|
+
}
|
|
67
|
+
return promise;
|
|
68
|
+
};
|
|
69
|
+
}
|
package/src/http/router.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Handler,
|
|
1
|
+
import { Handler, NextFunction, Request, Response, Router } from "express";
|
|
2
2
|
import {
|
|
3
3
|
Authorization,
|
|
4
4
|
Declaration,
|
|
@@ -11,12 +11,14 @@ import {
|
|
|
11
11
|
FeedResponse,
|
|
12
12
|
FeedsResponse,
|
|
13
13
|
Forbidden,
|
|
14
|
+
GraphDeserializer,
|
|
15
|
+
GraphSerializer,
|
|
16
|
+
GraphSource,
|
|
14
17
|
LoadMessage,
|
|
15
18
|
LoadResponse,
|
|
16
19
|
ProfileMessage,
|
|
17
20
|
ProjectedResult,
|
|
18
21
|
ReferencesByName,
|
|
19
|
-
SaveMessage,
|
|
20
22
|
Specification,
|
|
21
23
|
SpecificationParser,
|
|
22
24
|
Trace,
|
|
@@ -28,9 +30,8 @@ import {
|
|
|
28
30
|
parseLoadMessage,
|
|
29
31
|
parseSaveMessage
|
|
30
32
|
} from "jinaga";
|
|
31
|
-
|
|
32
33
|
import { Stream } from "./stream";
|
|
33
|
-
import {
|
|
34
|
+
import { createLineReader } from "./line-reader";
|
|
34
35
|
|
|
35
36
|
interface ParsedQs { [key: string]: undefined | string | string[] | ParsedQs | ParsedQs[] }
|
|
36
37
|
|
|
@@ -203,15 +204,12 @@ function postString<U>(method: (user: RequestUser, message: string) => Promise<U
|
|
|
203
204
|
}
|
|
204
205
|
|
|
205
206
|
function postCreate<T>(
|
|
206
|
-
parse: (
|
|
207
|
+
parse: (request: Request) => T,
|
|
207
208
|
method: (user: RequestUser, message: T) => Promise<void>
|
|
208
209
|
): Handler {
|
|
209
210
|
return (req, res, next) => {
|
|
210
211
|
const user = <RequestUser>req.user;
|
|
211
|
-
const message = parse(req
|
|
212
|
-
if (!message) {
|
|
213
|
-
throw new Error('Ensure that you have called app.use(express.json()).');
|
|
214
|
-
}
|
|
212
|
+
const message = parse(req);
|
|
215
213
|
method(user, message)
|
|
216
214
|
.then(_ => {
|
|
217
215
|
res.sendStatus(201);
|
|
@@ -274,6 +272,29 @@ function serializeUserIdentity(user: RequestUser | null): UserIdentity | null {
|
|
|
274
272
|
};
|
|
275
273
|
}
|
|
276
274
|
|
|
275
|
+
function inputSaveMessage(req: Request): GraphSource {
|
|
276
|
+
if (req.is('application/x-jinaga-graph-v1')) {
|
|
277
|
+
// Convert the request into a function that reads one line at a time.
|
|
278
|
+
const readLine = createLineReader(req);
|
|
279
|
+
|
|
280
|
+
return new GraphDeserializer(readLine);
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
return {
|
|
284
|
+
read: async (onEnvelopes) => {
|
|
285
|
+
const message = parseSaveMessage(req.body);
|
|
286
|
+
if (!message) {
|
|
287
|
+
throw new Error('Ensure that you have called app.use(express.json()).');
|
|
288
|
+
}
|
|
289
|
+
await onEnvelopes(message.facts.map(fact => ({
|
|
290
|
+
fact: fact,
|
|
291
|
+
signatures: []
|
|
292
|
+
})));
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
277
298
|
function outputGraph(result: FactEnvelope[], res: Response, accepts: (type: string) => string | false) {
|
|
278
299
|
if (accepts("application/x-jinaga-graph-v1")) {
|
|
279
300
|
res.type("application/x-jinaga-graph-v1");
|
|
@@ -306,7 +327,12 @@ export interface RequestUser {
|
|
|
306
327
|
export class HttpRouter {
|
|
307
328
|
handler: Handler;
|
|
308
329
|
|
|
309
|
-
constructor(
|
|
330
|
+
constructor(
|
|
331
|
+
private factManager: FactManager,
|
|
332
|
+
private authorization: Authorization,
|
|
333
|
+
private feedCache: FeedCache,
|
|
334
|
+
private allowedOrigin: string | string[] | ((origin: string, callback: (err: Error | null, allow?: boolean) => void) => void)
|
|
335
|
+
) {
|
|
310
336
|
const router = Router();
|
|
311
337
|
router.get('/login', getAuthenticate(user => this.login(user)));
|
|
312
338
|
router.post('/load', post(
|
|
@@ -315,8 +341,8 @@ export class HttpRouter {
|
|
|
315
341
|
outputGraph
|
|
316
342
|
));
|
|
317
343
|
router.post('/save', postCreate(
|
|
318
|
-
|
|
319
|
-
(user,
|
|
344
|
+
inputSaveMessage,
|
|
345
|
+
(user, graphSource) => this.save(user, graphSource)
|
|
320
346
|
));
|
|
321
347
|
|
|
322
348
|
router.post('/read', postString((user, input: string) => this.read(user, input)));
|
|
@@ -330,6 +356,30 @@ export class HttpRouter {
|
|
|
330
356
|
(user, params, query) => this.feed(user, params, query),
|
|
331
357
|
(user, params, query) => this.streamFeed(user, params, query)));
|
|
332
358
|
|
|
359
|
+
// Respond to OPTIONS requests to describe the methods and content types
|
|
360
|
+
// that are supported.
|
|
361
|
+
this.setOptions(router, '/login')
|
|
362
|
+
.intendedForGet()
|
|
363
|
+
.returningContent();
|
|
364
|
+
this.setOptions(router, '/load')
|
|
365
|
+
.intendedForPost('application/json')
|
|
366
|
+
.returningContent();
|
|
367
|
+
this.setOptions(router, '/save')
|
|
368
|
+
.intendedForPost('application/json', 'application/x-jinaga-graph-v1')
|
|
369
|
+
.returningNoContent();
|
|
370
|
+
this.setOptions(router, '/read')
|
|
371
|
+
.intendedForPost('text/plain')
|
|
372
|
+
.returningContent();
|
|
373
|
+
this.setOptions(router, '/write')
|
|
374
|
+
.intendedForPost('text/plain')
|
|
375
|
+
.returningNoContent();
|
|
376
|
+
this.setOptions(router, '/feeds')
|
|
377
|
+
.intendedForPost('text/plain')
|
|
378
|
+
.returningContent();
|
|
379
|
+
this.setOptions(router, '/feeds/:hash')
|
|
380
|
+
.intendedForGet()
|
|
381
|
+
.returningContent();
|
|
382
|
+
|
|
333
383
|
this.handler = router;
|
|
334
384
|
}
|
|
335
385
|
|
|
@@ -347,17 +397,14 @@ export class HttpRouter {
|
|
|
347
397
|
private async load(user: RequestUser, loadMessage: LoadMessage): Promise<FactEnvelope[]> {
|
|
348
398
|
const userIdentity = serializeUserIdentity(user);
|
|
349
399
|
const result = await this.authorization.load(userIdentity, loadMessage.references);
|
|
350
|
-
const facts = result.map(r => r.fact);
|
|
351
400
|
return result;
|
|
352
401
|
}
|
|
353
402
|
|
|
354
|
-
private async save(user: RequestUser | null,
|
|
403
|
+
private async save(user: RequestUser | null, graphSource: GraphSource): Promise<void> {
|
|
355
404
|
const userIdentity = serializeUserIdentity(user);
|
|
356
|
-
await
|
|
357
|
-
.
|
|
358
|
-
|
|
359
|
-
signatures: []
|
|
360
|
-
})));
|
|
405
|
+
await graphSource.read(async (envelopes) => {
|
|
406
|
+
await this.authorization.save(userIdentity, envelopes);
|
|
407
|
+
});
|
|
361
408
|
}
|
|
362
409
|
|
|
363
410
|
private async read(user: RequestUser | null, input: string): Promise<any[]> {
|
|
@@ -550,6 +597,77 @@ export class HttpRouter {
|
|
|
550
597
|
return declaredFact.declared.reference;
|
|
551
598
|
});
|
|
552
599
|
}
|
|
600
|
+
|
|
601
|
+
private setOptions(router: Router, path: string): OptionsConfiguration {
|
|
602
|
+
const addOptions = (allowedMethods: string[], allowedHeaders: string[], allowedTypes: string[]) => {
|
|
603
|
+
router.options(path, this.applyAllowOrigin((req, res) => {
|
|
604
|
+
res.set('Allow', allowedMethods.join(', '));
|
|
605
|
+
res.set('Access-Control-Allow-Methods', allowedMethods.join(', '));
|
|
606
|
+
res.set('Access-Control-Allow-Headers', allowedHeaders.join(', '));
|
|
607
|
+
if (allowedTypes.length > 0) {
|
|
608
|
+
res.set('Accept-Post', allowedTypes.join(', '));
|
|
609
|
+
}
|
|
610
|
+
res.status(204).send();
|
|
611
|
+
}));
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
return {
|
|
615
|
+
intendedForGet: () => {
|
|
616
|
+
addOptions(['GET', 'OPTIONS'], ['Accept', 'Authorization'], []);
|
|
617
|
+
return {
|
|
618
|
+
returningContent: () => { },
|
|
619
|
+
returningNoContent: () => { }
|
|
620
|
+
};
|
|
621
|
+
},
|
|
622
|
+
intendedForPost: (...contentTypes: string[]) => {
|
|
623
|
+
return {
|
|
624
|
+
returningContent: () => {
|
|
625
|
+
addOptions(
|
|
626
|
+
['POST', 'OPTIONS'],
|
|
627
|
+
['Content-Type', 'Accept', 'Authorization'],
|
|
628
|
+
contentTypes);
|
|
629
|
+
},
|
|
630
|
+
returningNoContent: () => {
|
|
631
|
+
addOptions(
|
|
632
|
+
['POST', 'OPTIONS'],
|
|
633
|
+
['Content-Type', 'Authorization'],
|
|
634
|
+
contentTypes);
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
private applyAllowOrigin(sendResponse: (req: Request, res: Response) => void) {
|
|
642
|
+
// Specify the allowed origins.
|
|
643
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
644
|
+
let requestOrigin = req.get('Origin');
|
|
645
|
+
|
|
646
|
+
if (typeof this.allowedOrigin === 'string') {
|
|
647
|
+
// If allowedOrigin is a string, use it as the value of the Access-Control-Allow-Origin header
|
|
648
|
+
res.set('Access-Control-Allow-Origin', this.allowedOrigin);
|
|
649
|
+
sendResponse(req, res);
|
|
650
|
+
} else if (Array.isArray(this.allowedOrigin)) {
|
|
651
|
+
// If allowedOrigin is an array, check if the request's origin is in the array
|
|
652
|
+
if (requestOrigin && this.allowedOrigin.includes(requestOrigin)) {
|
|
653
|
+
res.set('Access-Control-Allow-Origin', requestOrigin);
|
|
654
|
+
}
|
|
655
|
+
sendResponse(req, res);
|
|
656
|
+
} else if (typeof this.allowedOrigin === 'function' && requestOrigin) {
|
|
657
|
+
// If allowedOrigin is a function, call it with the request's origin
|
|
658
|
+
this.allowedOrigin(requestOrigin, (err, allow) => {
|
|
659
|
+
if (err) {
|
|
660
|
+
next(err);
|
|
661
|
+
} else if (allow) {
|
|
662
|
+
res.set('Access-Control-Allow-Origin', requestOrigin);
|
|
663
|
+
}
|
|
664
|
+
sendResponse(req, res);
|
|
665
|
+
});
|
|
666
|
+
} else {
|
|
667
|
+
sendResponse(req, res);
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
}
|
|
553
671
|
}
|
|
554
672
|
|
|
555
673
|
function parseString(input: any): string {
|
|
@@ -573,4 +691,14 @@ function extractResults(obj: any): any {
|
|
|
573
691
|
else {
|
|
574
692
|
return obj;
|
|
575
693
|
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
interface OptionsConfiguration {
|
|
697
|
+
intendedForGet(): ResponseConfiguration;
|
|
698
|
+
intendedForPost(...contentTypes: string[]): ResponseConfiguration;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
interface ResponseConfiguration {
|
|
702
|
+
returningContent(): void;
|
|
703
|
+
returningNoContent(): void;
|
|
576
704
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { AuthenticationDevice } from "./authentication/authentication-device";
|
|
|
2
2
|
export { AuthenticationSession } from "./authentication/authentication-session";
|
|
3
3
|
export { AuthorizationKeystore } from "./authorization/authorization-keystore";
|
|
4
4
|
export { DistributedFactCache } from "./authorization/distributed-fact-cache";
|
|
5
|
+
export { createLineReader } from "./http/line-reader";
|
|
5
6
|
export { HttpRouter, RequestUser } from "./http/router";
|
|
6
7
|
export { Stream } from "./http/stream";
|
|
7
8
|
export { JinagaServer, JinagaServerConfig, JinagaServerInstance, tracePool } from "./jinaga-server";
|
package/src/jinaga-server.ts
CHANGED
|
@@ -38,6 +38,7 @@ export type JinagaServerConfig = {
|
|
|
38
38
|
model?: Model,
|
|
39
39
|
authorization?: (a: AuthorizationRules) => AuthorizationRules,
|
|
40
40
|
distribution?: (d: DistributionRules) => DistributionRules,
|
|
41
|
+
origin?: string | string[] | ((origin: string, callback: (err: Error | null, allow?: boolean) => void) => void),
|
|
41
42
|
};
|
|
42
43
|
|
|
43
44
|
export type JinagaServerInstance = {
|
|
@@ -67,7 +68,7 @@ export class JinagaServer {
|
|
|
67
68
|
const network = new NetworkNoOp();
|
|
68
69
|
const factManager = new FactManager(fork, source, store, network);
|
|
69
70
|
const authorization = createAuthorization(authorizationRules, distributionRules, factManager, store, keystore);
|
|
70
|
-
const router = new HttpRouter(factManager, authorization, feedCache);
|
|
71
|
+
const router = new HttpRouter(factManager, authorization, feedCache, config.origin || '*');
|
|
71
72
|
const j: Jinaga = new Jinaga(authentication, factManager, syncStatusNotifier);
|
|
72
73
|
|
|
73
74
|
async function close() {
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { FactEnvelope } from "jinaga";
|
|
2
|
-
export declare class GraphSerializer {
|
|
3
|
-
private readonly write;
|
|
4
|
-
private index;
|
|
5
|
-
private indexByFactReference;
|
|
6
|
-
private publicKeys;
|
|
7
|
-
constructor(write: (chunk: string) => void);
|
|
8
|
-
serialize(result: FactEnvelope[]): void;
|
|
9
|
-
private getPredecessorIndexes;
|
|
10
|
-
private getFactIndex;
|
|
11
|
-
}
|
package/dist/http/serializer.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GraphSerializer = void 0;
|
|
4
|
-
class GraphSerializer {
|
|
5
|
-
constructor(write) {
|
|
6
|
-
this.write = write;
|
|
7
|
-
this.index = 0;
|
|
8
|
-
this.indexByFactReference = {};
|
|
9
|
-
this.publicKeys = [];
|
|
10
|
-
}
|
|
11
|
-
serialize(result) {
|
|
12
|
-
// Write the facts
|
|
13
|
-
for (const fact of result) {
|
|
14
|
-
// Skip facts that have already been written
|
|
15
|
-
const key = fact.fact.type + ":" + fact.fact.hash;
|
|
16
|
-
if (this.indexByFactReference.hasOwnProperty(key)) {
|
|
17
|
-
continue;
|
|
18
|
-
}
|
|
19
|
-
// Write any new public keys
|
|
20
|
-
for (const signature of fact.signatures) {
|
|
21
|
-
if (!this.publicKeys.includes(signature.publicKey)) {
|
|
22
|
-
const pkIndex = this.publicKeys.length;
|
|
23
|
-
const publicKey = JSON.stringify(signature.publicKey);
|
|
24
|
-
this.write(`PK${pkIndex.toString()}\n${publicKey}\n\n`);
|
|
25
|
-
this.publicKeys.push(signature.publicKey);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
// Write the fact
|
|
29
|
-
const factType = JSON.stringify(fact.fact.type);
|
|
30
|
-
const predecessorIndexes = JSON.stringify(this.getPredecessorIndexes(fact.fact.predecessors));
|
|
31
|
-
const factFields = JSON.stringify(fact.fact.fields);
|
|
32
|
-
let output = `${factType}\n${predecessorIndexes}\n${factFields}`;
|
|
33
|
-
// Write the signatures
|
|
34
|
-
for (const signature of fact.signatures) {
|
|
35
|
-
const publicKeyIndex = this.publicKeys.indexOf(signature.publicKey);
|
|
36
|
-
const publicKey = `PK${publicKeyIndex.toString()}`;
|
|
37
|
-
const signatureString = JSON.stringify(signature.signature);
|
|
38
|
-
output += `\n${publicKey}\n${signatureString}`;
|
|
39
|
-
}
|
|
40
|
-
output += "\n\n";
|
|
41
|
-
this.write(output);
|
|
42
|
-
this.indexByFactReference[key] = this.index;
|
|
43
|
-
this.index++;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
getPredecessorIndexes(predecessors) {
|
|
47
|
-
const result = {};
|
|
48
|
-
for (const role in predecessors) {
|
|
49
|
-
const reference = predecessors[role];
|
|
50
|
-
if (Array.isArray(reference)) {
|
|
51
|
-
result[role] = reference.map(r => this.getFactIndex(r));
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
result[role] = this.getFactIndex(reference);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return result;
|
|
58
|
-
}
|
|
59
|
-
getFactIndex(reference) {
|
|
60
|
-
const key = reference.type + ":" + reference.hash;
|
|
61
|
-
if (!this.indexByFactReference.hasOwnProperty(key)) {
|
|
62
|
-
throw new Error(`Fact reference not found in graph: ${key}`);
|
|
63
|
-
}
|
|
64
|
-
return this.indexByFactReference[key];
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
exports.GraphSerializer = GraphSerializer;
|
|
68
|
-
//# sourceMappingURL=serializer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"serializer.js","sourceRoot":"","sources":["../../src/http/serializer.ts"],"names":[],"mappings":"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"}
|