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 +49 -0
- package/bin/inbox-server.js +36 -0
- package/config/offer_schema.json +40 -0
- package/examples/offer.jsonld +17 -0
- package/handler/demo.js +22 -0
- package/index.js +102 -0
- package/package.json +27 -0
- package/public/index.html +52 -0
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
|
+
}
|
package/handler/demo.js
ADDED
|
@@ -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>
|