ldn-inbox-server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # ldn-inbox-server
2
+
3
+ An experimental LDN inbox server for [Event Notification](https://www.eventnotifications.net) messages.
4
+
5
+ ## Install
6
+
7
+ ```
8
+ yarn add ldn-inbox-server
9
+ ```
10
+
11
+ ## Example
12
+
13
+ Create required directories
14
+
15
+ ```
16
+ mkdir config inbox public
17
+ ```
18
+
19
+ Copy an example JSON Schema as `config/offer_schema.json` from this project.
20
+
21
+ Start the server:
22
+
23
+ ```
24
+ npx ldn-inbox-server start-server --port 8000
25
+ ```
26
+
27
+ Send a demonstration Event Notifications message:
28
+
29
+ ```
30
+ curl -X POST -H 'Content-Type: application/ld+json' --data-binary '@examples/offer.jsonld' http://localhost:8000/inbox/
31
+ ```
32
+
33
+ Start an inbox handler:
34
+
35
+ ```
36
+ npx ldn-inbox-server handle-inbox ./handler/demo.js
37
+ ```
38
+
39
+ where `./handler/demo.js` contains a `handleInbox` function to process the inbox/
40
+
41
+ Example handler:
42
+
43
+ ```
44
+ async function handleInbox(path,options) {
45
+ console.log(path);
46
+ }
47
+
48
+ module.exports = { handleInbox };
49
+ ```
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { program } = require('commander');
4
+ const { inbox_server, handle_inbox } = require('../index.js');
5
+
6
+ const HOST = 'localhost'
7
+ const PORT = 8000;
8
+ const PUBLIC_PATH = './public';
9
+ const INBOX_PATH = './inbox';
10
+ const JSON_SCHEMA_PATH = './config/offer_schema.json';
11
+
12
+ program
13
+ .name('lnd-inbox-server')
14
+ .version('1.0.0')
15
+ .description('A demonstration Event Notifications Inbox server');
16
+
17
+ program
18
+ .command('start-server')
19
+ .option('--host <host>','host',HOST)
20
+ .option('--port <port>','port',PORT)
21
+ .option('--inbox <inbox>','inbox',INBOX_PATH)
22
+ .option('--public <public>','public',PUBLIC_PATH)
23
+ .option('--schema <schema>','json schema',JSON_SCHEMA_PATH)
24
+ .action( (options) => {
25
+ inbox_server(options);
26
+ });
27
+
28
+ program
29
+ .command('handle-inbox')
30
+ .option('--inbox <inbox>','inbox',INBOX_PATH)
31
+ .argument('<handler>','handler of inbox')
32
+ .action( async(handler,options) => {
33
+ await handle_inbox(options['inbox'],handler,options);
34
+ });
35
+
36
+ program.parse();
@@ -0,0 +1,40 @@
1
+ {
2
+ "id": "/EventNotificationsOffer",
3
+ "type": "object",
4
+ "properties": {
5
+ "id": {"type": "string"},
6
+ "type": {"type": "string"},
7
+ "actor": {
8
+ "type": "object",
9
+ "properties": {
10
+ "id": {"type": "string"},
11
+ "type": {"type": "string"},
12
+ "inbox": {"type": "string"}
13
+ },
14
+ "required": [ "id", "inbox" ]
15
+ },
16
+ "object": {
17
+ "type": "object",
18
+ "properties": {
19
+ "id": {"type": "string"}
20
+ },
21
+ "oneOf": [
22
+ {
23
+ "properties" : {
24
+ "type": {"type": "string"}
25
+ }
26
+ },
27
+ {
28
+ "properties" : {
29
+ "type": {
30
+ "type": "array",
31
+ "items": {"type": "string"}
32
+ }
33
+ }
34
+ }
35
+ ],
36
+ "required": [ "id" ]
37
+ }
38
+ },
39
+ "required": ["id", "type", "actor", "object"]
40
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "@context": [
3
+ "https://www.w3.org/ns/activitystreams" ,
4
+ {"schema": "https://schema.org/"}
5
+ ],
6
+ "id": "urn:uuid:6E5FAF88-A7F1-47A4-B087-77345EBFF495",
7
+ "type": "Offer",
8
+ "actor": {
9
+ "id": "https://acme.org/profile/card#us",
10
+ "inbox": "https://acme.org/inbox/",
11
+ "type": "Organization"
12
+ },
13
+ "object": {
14
+ "id": "http://acme.org/artifacts/alice/data-set-2022-01-19.zip",
15
+ "type": [ "Document" , "schema:Dataset" ]
16
+ }
17
+ }
@@ -0,0 +1,22 @@
1
+ const fs = require('fs');
2
+
3
+ async function handleInbox(path,options) {
4
+ fs.readdir(path, (err,files) => {
5
+ files.forEach( (file) => {
6
+ const fullPath = `${path}/${file}`;
7
+ if (file.match("^\\..*$")) {
8
+ // Ignore
9
+ }
10
+ else if (file.match("^.*\\.jsond$")) {
11
+ // Process
12
+ console.log(`Deleting ${fullPath}...`);
13
+ fs.unlinkSync(fullPath);
14
+ }
15
+ else {
16
+ fs.unlinkSync(fullPath);
17
+ }
18
+ });
19
+ });
20
+ }
21
+
22
+ module.exports = { handleInbox };
package/index.js ADDED
@@ -0,0 +1,102 @@
1
+ const { start_server } = require('mellon-server');
2
+ const Validator = require('jsonschema').Validator;
3
+ const fs = require('fs');
4
+ const md5 = require('md5');
5
+
6
+ let INBOX_PATH = './inbox';
7
+ let JSON_SCHEMA = '';
8
+
9
+ function inbox_server(options) {
10
+ INBOX = options['inbox'];
11
+ JSON_SCHEMA = JSON.parse(fs.readFileSync(options['schema'], { encoding: 'utf-8'}));
12
+ start_server({
13
+ host: options['host'],
14
+ port: options['port'],
15
+ public: options['public'],
16
+ registry: [
17
+ { path : 'inbox/.*' , do: doInbox }
18
+ ]
19
+ });
20
+ }
21
+
22
+ async function handle_inbox(path,handler,options) {
23
+ delete require.cache[handler];
24
+ const func = require(handler).handleInbox;
25
+ await func(path,options);
26
+ }
27
+
28
+ function doInbox(req,res) {
29
+ if (req.method !== 'POST') {
30
+ res.writeHead(403);
31
+ res.end('Forbidden');
32
+ return;
33
+ }
34
+
35
+ const headers = req.headers;
36
+
37
+ if (headers && (
38
+ headers['content-type'] === 'application/ld+json' ||
39
+ headers['content-type'] === 'application/json'
40
+ )
41
+ ) {
42
+ // We are ok
43
+ }
44
+ else {
45
+ res.writeHead(400);
46
+ res.end(`Need a Content-Type 'application/ld+json'`);
47
+ return;
48
+ }
49
+
50
+ let postData = ''
51
+ req.on('data', (data) => {
52
+ postData += data;
53
+ });
54
+ req.on('end',() => {
55
+ if (checkBody(postData)) {
56
+ const id = storeBody(postData);
57
+ res.setHeader('Location',`${req.url}${id}`);
58
+ res.writeHead(201);
59
+ res.end(`Accepted ${req.url}${id}`);
60
+ }
61
+ else {
62
+ res.writeHead(400);
63
+ res.end(`Looks like a weird POST to me...`);
64
+ }
65
+ });
66
+ }
67
+
68
+ function storeBody(data) {
69
+ try {
70
+ const id = md5(data);
71
+ const newpath = `${INBOX_PATH}/${id}.jsonld`;
72
+
73
+ if (! fs.existsSync(newpath)) {
74
+ fs.writeFileSync(newpath,data);
75
+ }
76
+
77
+ return `${id}.jsonld`;
78
+ }
79
+ catch (e) {
80
+ return null;
81
+ }
82
+ }
83
+
84
+ function checkBody(data) {
85
+ try {
86
+ const json = JSON.parse(data);
87
+ const v = new Validator();
88
+ const res = v.validate(json,JSON_SCHEMA);
89
+
90
+ if (res.errors.length == 0) {
91
+ return true;
92
+ }
93
+ else {
94
+ return false;
95
+ }
96
+ }
97
+ catch (e) {
98
+ return false;
99
+ }
100
+ }
101
+
102
+ module.exports = { inbox_server , handle_inbox };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "ldn-inbox-server",
3
+ "version": "1.0.0",
4
+ "description": "A demonstration Event Notifications Inbox server",
5
+ "main": "index.js",
6
+ "author": "Patrick Hochstenbach <Patrick.Hochstenbach@UGent.be>",
7
+ "repository": "https://github.com/MellonScholarlyCommunication/ldn-inbox-server",
8
+ "scripts": {
9
+ "server": "node bin/inbox-server.js start-server",
10
+ "demo-post": "curl -X POST -H 'Content-Type: application/ld+json' --data-binary '@examples/offer.jsonld' http://localhost:8000/inbox/",
11
+ "demo-orch": "npx orch --info --keep --in inbox --out outbox rules/demo.n3",
12
+ "demo-pol": "npx pol --info --keep --in outbox"
13
+ },
14
+ "bin": "./bin/inbox-server.js",
15
+ "keywords": [
16
+ "ldn",
17
+ "event notifications"
18
+ ],
19
+ "license": "MIT",
20
+ "dependencies": {
21
+ "commander": "^12.0.0",
22
+ "jsonschema": "^1.4.1",
23
+ "md5": "^2.3.0",
24
+ "mellon-server": "^1.0.2",
25
+ "uuid": "^9.0.1"
26
+ }
27
+ }
@@ -0,0 +1,52 @@
1
+ <html>
2
+ <head>
3
+ <link
4
+ rel="stylesheet"
5
+ href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
6
+ />
7
+ <title>LDN Inbox Server</title>
8
+ </head>
9
+ <body>
10
+ <main class="container">
11
+ <h1>LDN Inbox Server</h1>
12
+
13
+ <h2>API</h2>
14
+
15
+ <h3>/inbox/</h3>
16
+
17
+ <article>
18
+ An <a href="https://www.eventnotifications.net">Event Notifications</a>
19
+ LDN Inbox accepting Event Notification Offers.
20
+
21
+ <p>
22
+ <h4>Example</h4>
23
+ <pre>
24
+ curl -X POST -H 'Content-Type: application/ld+json' --data-binary '@examples/offer.jsonld' http://localhost:8000/inbox/
25
+ </pre>
26
+
27
+ where <tt>offer.jsonld</tt>:
28
+
29
+ <pre>
30
+ {
31
+ "@context": [
32
+ "https://www.w3.org/ns/activitystreams" ,
33
+ {"schema": "https://schema.org/"}
34
+ ],
35
+ "id": "urn:uuid:6E5FAF88-A7F1-47A4-B087-77345EBFF495",
36
+ "type": "Offer",
37
+ "actor": {
38
+ "id": "https://acme.org/profile/card#us",
39
+ "inbox": "https://acme.org/inbox/",
40
+ "type": "Organization"
41
+ },
42
+ "object": {
43
+ "id": "http://acme.org/artifacts/alice/data-set-2022-01-19.zip",
44
+ "type": [ "Document" , "schema:Dataset" ]
45
+ }
46
+ }
47
+ </pre>
48
+ </p>
49
+ </article>
50
+ </main>
51
+ </body>
52
+ </html>