ldn-inbox-server 1.4.4 → 1.4.6
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 +30 -2
- package/bin/ldn-inbox-server.js +43 -2
- package/config/inbox_config.json +8 -0
- package/handler/notification_handler/offer_memento.js +4 -4
- package/lib/index.js +43 -76
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -55,7 +55,35 @@ npx ldn-inbox-server handler @outbox -hn ./handler/send_notification_handler.js
|
|
|
55
55
|
- `LDN_SERVER_JSON_SCHEMA` : notification JSON validation schema
|
|
56
56
|
- `LDN_SERVER_BASEURL` : baseurl of the LDN inbox server
|
|
57
57
|
- `LDN_SERVER_INBOX_GLOB` : glob of files to process in inbox directory
|
|
58
|
-
- `LDN_SERVER_HAS_PUBLIC_INBOX` : if true, then public read access is allowed on inbox
|
|
58
|
+
- `LDN_SERVER_HAS_PUBLIC_INBOX` : if true, then public read access is allowed on the inbox
|
|
59
|
+
- `LDN_SERVER_HAS_WRITABLE_INBOX` : if true, then public write access is allowed on the inbox
|
|
60
|
+
|
|
61
|
+
## Multiple inboxes
|
|
62
|
+
|
|
63
|
+
Instead of a single inbox, multiple inboxes can be configured by adding a JSON configuration file to the installation. The JSON file should contain a `registry` entry with contains an array of inbox configuration. An example:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
{
|
|
67
|
+
"registry": [
|
|
68
|
+
{ "path": "inbox/.*" ,
|
|
69
|
+
"with": {
|
|
70
|
+
"url": "inbox/",
|
|
71
|
+
"inbox": "./inbox",
|
|
72
|
+
"inboxPublic": 1,
|
|
73
|
+
"inboxWritable": 1,
|
|
74
|
+
"schema": "./config/schema1.json"
|
|
75
|
+
}},
|
|
76
|
+
{ "path": "inbox2/.*" ,
|
|
77
|
+
"with": {
|
|
78
|
+
"url": "inbox2/",
|
|
79
|
+
"inbox": "./inbox2",
|
|
80
|
+
"inboxPublic": 1,
|
|
81
|
+
"inboxWritable": 0,
|
|
82
|
+
"schema": "./config/schema2.json"
|
|
83
|
+
}}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
```
|
|
59
87
|
|
|
60
88
|
## Extend
|
|
61
89
|
|
|
@@ -115,7 +143,7 @@ A handler can be started on any directory. E.g. a workflow might be:
|
|
|
115
143
|
|
|
116
144
|
A handler that creates for an incoming notification an `Accept` notification in the `@outbox`.
|
|
117
145
|
|
|
118
|
-
###
|
|
146
|
+
### Eventlog handler
|
|
119
147
|
|
|
120
148
|
A handler that updates an event log with the incoming notification.
|
|
121
149
|
|
package/bin/ldn-inbox-server.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const { program } = require('commander');
|
|
5
|
-
const {
|
|
5
|
+
const { start_server } = require('mellon-server');
|
|
6
|
+
const { doInbox } = require('../lib/index');
|
|
6
7
|
const { handle_inbox } = require('../lib/handler');
|
|
7
8
|
require('dotenv').config();
|
|
8
9
|
|
|
@@ -20,6 +21,8 @@ const JSON_SCHEMA_PATH = process.env.LDN_SERVER_JSON_SCHEMA ?? './config/notific
|
|
|
20
21
|
const INBOX_CONFIG = process.env.LDN_SERVER_INBOX_CONFIG;
|
|
21
22
|
const OUTBOX_CONFIG = process.env.LDN_SERVER_OUTBOX_CONFIG;
|
|
22
23
|
const HAS_PUBLIC = process.env.LDN_SERVER_HAS_PUBLIC_INBOX ?? 0;
|
|
24
|
+
const HAS_WRITE = process.env.LDN_SERVER_HAS_WRITABLE_INBOX ?? 1;
|
|
25
|
+
const OTHER_CONFIG = process.env.LDN_SERVER_OTHER_CONFIG;
|
|
23
26
|
|
|
24
27
|
program
|
|
25
28
|
.name('lnd-inbox-server')
|
|
@@ -29,6 +32,7 @@ program
|
|
|
29
32
|
.command('start-server')
|
|
30
33
|
.option('--host <host>','host',HOST)
|
|
31
34
|
.option('--port <port>','port',PORT)
|
|
35
|
+
.option('--config <config>','config',OTHER_CONFIG)
|
|
32
36
|
.option('--url <path>','path',INBOX_URL)
|
|
33
37
|
.option('--base <url>','base url',INBOX_BASE_URL)
|
|
34
38
|
.option('--inbox <inbox>','inbox',INBOX_PATH)
|
|
@@ -36,8 +40,45 @@ program
|
|
|
36
40
|
.option('--schema <schema>','json schema',JSON_SCHEMA_PATH)
|
|
37
41
|
.option('--registry <registry>','registry',null)
|
|
38
42
|
.option('--inbox-public','public readable inbox',HAS_PUBLIC)
|
|
43
|
+
.option('--inbox-writeable','public writable inbox',HAS_WRITE)
|
|
39
44
|
.action( (options) => {
|
|
40
|
-
|
|
45
|
+
let registry = [];
|
|
46
|
+
|
|
47
|
+
// Add other (external defined) registry parts
|
|
48
|
+
if (options['registry']) {
|
|
49
|
+
const path = options['registry'];
|
|
50
|
+
let registry2;
|
|
51
|
+
if (typeof path === 'string' || path instanceof String) {
|
|
52
|
+
registry2 = JSON.parse(fs.readFileSync(path,{ encoding: 'utf-8'}));
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
registry2 = path;
|
|
56
|
+
}
|
|
57
|
+
registry = registry.concat(registry2);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (options['config'] && fs.existsSync(options['config'])) {
|
|
61
|
+
const config = JSON.parse(fs.readFileSync(options['config'], { encoding: 'utf-8'}));
|
|
62
|
+
if (config.registry) {
|
|
63
|
+
for (let i = 0 ; i < config.registry.length ; i++) {
|
|
64
|
+
const registry_item = config.registry[i];
|
|
65
|
+
registry_item['do'] = doInbox;
|
|
66
|
+
registry_item['with'] = {...options,...registry_item['with']};
|
|
67
|
+
registry = registry.concat(registry_item);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
registry = registry.concat({ path : `${options['url']}.*` , do: doInbox , with: options});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
start_server({
|
|
76
|
+
host: options['host'],
|
|
77
|
+
port: options['port'],
|
|
78
|
+
base: options['base'],
|
|
79
|
+
public: options['public'],
|
|
80
|
+
registry: registry
|
|
81
|
+
});
|
|
41
82
|
});
|
|
42
83
|
|
|
43
84
|
program
|
package/config/inbox_config.json
CHANGED
|
@@ -11,22 +11,22 @@ async function handle({path,options,config}) {
|
|
|
11
11
|
logger.info(`parsing notification ${path}`);
|
|
12
12
|
|
|
13
13
|
if (! config) {
|
|
14
|
-
logger.error('no configuration found for
|
|
14
|
+
logger.error('no configuration found for offer_memento');
|
|
15
15
|
return { path, options, success: false };
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
if (!config['actor'] || !config['target']) {
|
|
19
|
-
logger.error('no actor/target entry
|
|
19
|
+
logger.error('no actor/target entry');
|
|
20
20
|
return { path, options, success: false };
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
if (!options['artifact']) {
|
|
24
|
-
logger.info(`no artifact found (
|
|
24
|
+
logger.info(`no artifact found (ignoring this request)`);
|
|
25
25
|
return { path, options, success: true };
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
if (!options['eventlog']) {
|
|
29
|
-
logger.info(`no artifact found (
|
|
29
|
+
logger.info(`no artifact found (ignoring this request)`);
|
|
30
30
|
return { path, options, success: true };
|
|
31
31
|
}
|
|
32
32
|
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
2
|
const Validator = require('jsonschema').Validator;
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const md5 = require('md5');
|
|
@@ -7,56 +7,21 @@ const {
|
|
|
7
7
|
fetchOriginal,
|
|
8
8
|
backOff_fetch,
|
|
9
9
|
sendNotification,
|
|
10
|
-
moveTo
|
|
10
|
+
moveTo,
|
|
11
|
+
parseAsJSON
|
|
11
12
|
} = require('../lib/util');
|
|
12
13
|
const { handle_inbox } = require('../lib/handler');
|
|
13
14
|
const logger = getLogger();
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
let INBOX_PUBLIC_READABLE = 0;
|
|
19
|
-
let JSON_SCHEMA = '';
|
|
20
|
-
|
|
21
|
-
function inbox_server(options) {
|
|
22
|
-
INBOX_URL = options['url'];
|
|
23
|
-
INBOX_PATH = options['inbox'];
|
|
24
|
-
INBOX_BASE_URL = options['base'];
|
|
25
|
-
INBOX_PUBLIC_READABLE = options['inboxPublic'];
|
|
26
|
-
JSON_SCHEMA = JSON.parse(fs.readFileSync(options['schema'], { encoding: 'utf-8'}));
|
|
27
|
-
let registry = [{ path : `${INBOX_URL}.*` , do: doInbox }];
|
|
28
|
-
|
|
29
|
-
// Add other (external defined) registry parts
|
|
30
|
-
if (options['registry']) {
|
|
31
|
-
const path = options['registry'];
|
|
32
|
-
let registry2;
|
|
33
|
-
if (typeof path === 'string' || path instanceof String) {
|
|
34
|
-
registry2 = JSON.parse(fs.readFileSync(path,{ encoding: 'utf-8'}));
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
registry2 = path;
|
|
38
|
-
}
|
|
39
|
-
registry = registry.concat(registry2);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
start_server({
|
|
43
|
-
host: options['host'],
|
|
44
|
-
port: options['port'],
|
|
45
|
-
base: options['base'],
|
|
46
|
-
public: options['public'],
|
|
47
|
-
registry: registry
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function doInbox(req,res) {
|
|
52
|
-
if (req.method === 'GET' && INBOX_PUBLIC_READABLE == 1) {
|
|
53
|
-
doInboxGET(req,res);
|
|
16
|
+
function doInbox(req,res,opts) {
|
|
17
|
+
if (req.method === 'GET' && opts['inboxPublic'] == 1) {
|
|
18
|
+
doInboxGET(req,res,opts);
|
|
54
19
|
}
|
|
55
|
-
else if (req.method == 'HEAD' &&
|
|
56
|
-
doInboxHEAD(req,res);
|
|
20
|
+
else if (req.method == 'HEAD' && opts['inboxPublic'] == 1) {
|
|
21
|
+
doInboxHEAD(req,res,opts);
|
|
57
22
|
}
|
|
58
|
-
else if (req.method === 'POST') {
|
|
59
|
-
doInboxPOST(req,res);
|
|
23
|
+
else if (req.method === 'POST' && opts['inboxWriteable'] == 1) {
|
|
24
|
+
doInboxPOST(req,res,opts);
|
|
60
25
|
}
|
|
61
26
|
else {
|
|
62
27
|
logger.error(`tried method ${req.method} on inbox : forbidden`);
|
|
@@ -65,16 +30,16 @@ function doInbox(req,res) {
|
|
|
65
30
|
}
|
|
66
31
|
}
|
|
67
32
|
|
|
68
|
-
function doInboxGET(req,res) {
|
|
69
|
-
const pathItem = req.url.substring(
|
|
33
|
+
function doInboxGET(req,res,opts) {
|
|
34
|
+
const pathItem = req.url.substring(opts['url'].length);
|
|
70
35
|
|
|
71
36
|
logger.debug(`doInboxGET (for ${pathItem})`);
|
|
72
37
|
|
|
73
38
|
if (pathItem === '/') {
|
|
74
|
-
doInboxGET_Index(req,res);
|
|
39
|
+
doInboxGET_Index(req,res,opts);
|
|
75
40
|
}
|
|
76
41
|
else if (pathItem.match(/^\/[A-Za-z0-9_-]+\.jsonld$/)) {
|
|
77
|
-
doInboxGET_Read(req,res);
|
|
42
|
+
doInboxGET_Read(req,res,opts);
|
|
78
43
|
}
|
|
79
44
|
else {
|
|
80
45
|
res.writeHead(403);
|
|
@@ -82,14 +47,14 @@ function doInboxGET(req,res) {
|
|
|
82
47
|
}
|
|
83
48
|
}
|
|
84
49
|
|
|
85
|
-
function doInboxHEAD(req,res) {
|
|
86
|
-
const pathItem = req.url.substring(
|
|
50
|
+
function doInboxHEAD(req,res,opts) {
|
|
51
|
+
const pathItem = req.url.substring(opts['url'].length);
|
|
87
52
|
const id = pathItem.substring(1);
|
|
88
53
|
|
|
89
54
|
logger.debug(`doInboxHEAD (for ${pathItem})`);
|
|
90
55
|
|
|
91
56
|
if (pathItem === '/') {
|
|
92
|
-
const meta = getBody(`${id}.meta
|
|
57
|
+
const meta = getBody(`${id}.meta`,opts);
|
|
93
58
|
|
|
94
59
|
if (meta) {
|
|
95
60
|
const metadata = JSON.parse(meta);
|
|
@@ -106,7 +71,7 @@ function doInboxHEAD(req,res) {
|
|
|
106
71
|
}
|
|
107
72
|
|
|
108
73
|
if (pathItem.match(/^\/[A-Za-z0-9_-]+\.jsonld$/)) {
|
|
109
|
-
const result = getBody(id);
|
|
74
|
+
const result = getBody(id,opts);
|
|
110
75
|
|
|
111
76
|
if (! result) {
|
|
112
77
|
res.writeHead(403);
|
|
@@ -114,7 +79,7 @@ function doInboxHEAD(req,res) {
|
|
|
114
79
|
return;
|
|
115
80
|
}
|
|
116
81
|
|
|
117
|
-
const meta = getBody(`${id}.meta
|
|
82
|
+
const meta = getBody(`${id}.meta`,opts);
|
|
118
83
|
|
|
119
84
|
if (meta) {
|
|
120
85
|
const metadata = JSON.parse(meta);
|
|
@@ -137,18 +102,18 @@ function doInboxHEAD(req,res) {
|
|
|
137
102
|
}
|
|
138
103
|
}
|
|
139
104
|
|
|
140
|
-
function doInboxGET_Index(req,res) {
|
|
141
|
-
const notifications = listInbox().map( (e) => {
|
|
142
|
-
return
|
|
105
|
+
function doInboxGET_Index(req,res,opts) {
|
|
106
|
+
const notifications = listInbox(opts).map( (e) => {
|
|
107
|
+
return opts['base'] + '/' + opts['url'] + e;
|
|
143
108
|
});
|
|
144
109
|
|
|
145
110
|
const result = {
|
|
146
111
|
"@context": "http://www.w3.org/ns/ldp",
|
|
147
|
-
"@id":
|
|
112
|
+
"@id": opts['base'] + '/' + opts['url'],
|
|
148
113
|
"contains": notifications
|
|
149
114
|
};
|
|
150
115
|
|
|
151
|
-
const meta = getBody(`.meta
|
|
116
|
+
const meta = getBody(`.meta`,opts);
|
|
152
117
|
|
|
153
118
|
if (meta) {
|
|
154
119
|
const metadata = JSON.parse(meta);
|
|
@@ -164,13 +129,13 @@ function doInboxGET_Index(req,res) {
|
|
|
164
129
|
res.end(JSON.stringify(result,null,2));
|
|
165
130
|
}
|
|
166
131
|
|
|
167
|
-
function doInboxGET_Read(req,res) {
|
|
168
|
-
const pathItem = req.url.substring(
|
|
132
|
+
function doInboxGET_Read(req,res,opts) {
|
|
133
|
+
const pathItem = req.url.substring(opts['url'].length);
|
|
169
134
|
const id = pathItem.substring(1);
|
|
170
|
-
const result = getBody(id);
|
|
135
|
+
const result = getBody(id,opts);
|
|
171
136
|
|
|
172
137
|
if (result) {
|
|
173
|
-
const meta = getBody(`${id}.meta
|
|
138
|
+
const meta = getBody(`${id}.meta`,opts);
|
|
174
139
|
|
|
175
140
|
if (meta) {
|
|
176
141
|
const metadata = JSON.parse(meta);
|
|
@@ -191,8 +156,8 @@ function doInboxGET_Read(req,res) {
|
|
|
191
156
|
}
|
|
192
157
|
}
|
|
193
158
|
|
|
194
|
-
function doInboxPOST(req,res) {
|
|
195
|
-
const pathItem = req.url.substring(
|
|
159
|
+
function doInboxPOST(req,res,opts) {
|
|
160
|
+
const pathItem = req.url.substring(opts['url'].length);
|
|
196
161
|
|
|
197
162
|
if (pathItem !== '/') {
|
|
198
163
|
req.writeHead(403);
|
|
@@ -222,8 +187,8 @@ function doInboxPOST(req,res) {
|
|
|
222
187
|
});
|
|
223
188
|
req.on('end',() => {
|
|
224
189
|
logger.debug(postData);
|
|
225
|
-
if (checkBody(postData)) {
|
|
226
|
-
const id = storeBody(postData);
|
|
190
|
+
if (checkBody(postData,opts)) {
|
|
191
|
+
const id = storeBody(postData,opts);
|
|
227
192
|
logger.debug(`accepted ${req.url}${id}`);
|
|
228
193
|
res.setHeader('Location',`${req.url}${id}`);
|
|
229
194
|
res.writeHead(201);
|
|
@@ -237,13 +202,13 @@ function doInboxPOST(req,res) {
|
|
|
237
202
|
});
|
|
238
203
|
}
|
|
239
204
|
|
|
240
|
-
function listInbox() {
|
|
205
|
+
function listInbox(opts) {
|
|
241
206
|
const glob = new RegExp("^.*\\.jsonld$");
|
|
242
207
|
|
|
243
208
|
logger.debug(`listInbox()`);
|
|
244
209
|
|
|
245
210
|
try {
|
|
246
|
-
const entries = fs.readdirSync(
|
|
211
|
+
const entries = fs.readdirSync(opts['inbox']).filter( (file) => {
|
|
247
212
|
return file.match(glob);
|
|
248
213
|
});
|
|
249
214
|
return entries;
|
|
@@ -254,21 +219,21 @@ function listInbox() {
|
|
|
254
219
|
}
|
|
255
220
|
}
|
|
256
221
|
|
|
257
|
-
function getBody(id) {
|
|
222
|
+
function getBody(id,opts) {
|
|
258
223
|
logger.debug(`getBody(${id})`);
|
|
259
224
|
|
|
260
225
|
try {
|
|
261
|
-
return fs.readFileSync(
|
|
226
|
+
return fs.readFileSync(opts['inbox'] + '/' + id, {encoding : 'utf-8'});
|
|
262
227
|
}
|
|
263
228
|
catch(e) {
|
|
264
229
|
return null;
|
|
265
230
|
}
|
|
266
231
|
}
|
|
267
232
|
|
|
268
|
-
function storeBody(data) {
|
|
233
|
+
function storeBody(data,opts) {
|
|
269
234
|
try {
|
|
270
235
|
const id = md5(data);
|
|
271
|
-
const newpath = `${
|
|
236
|
+
const newpath = `${opts['inbox']}/${id}.jsonld`;
|
|
272
237
|
|
|
273
238
|
if (! fs.existsSync(newpath)) {
|
|
274
239
|
logger.debug(`storing ${newpath}`);
|
|
@@ -285,12 +250,13 @@ function storeBody(data) {
|
|
|
285
250
|
}
|
|
286
251
|
}
|
|
287
252
|
|
|
288
|
-
function checkBody(data) {
|
|
289
|
-
if (!
|
|
253
|
+
function checkBody(data,opts) {
|
|
254
|
+
if (! (opts['schema'] && fs.existsSync(opts['schema']))) {
|
|
290
255
|
logger.debug(`no JSON_SCHEMA found`);
|
|
291
256
|
return true;
|
|
292
257
|
}
|
|
293
258
|
try {
|
|
259
|
+
const JSON_SCHEMA = JSON.parse(fs.readFileSync(opts['schema'], { encoding: 'utf-8'}));
|
|
294
260
|
const json = JSON.parse(data);
|
|
295
261
|
const v = new Validator();
|
|
296
262
|
const res = v.validate(json,JSON_SCHEMA);
|
|
@@ -310,8 +276,9 @@ function checkBody(data) {
|
|
|
310
276
|
}
|
|
311
277
|
|
|
312
278
|
module.exports = {
|
|
313
|
-
|
|
279
|
+
doInbox,
|
|
314
280
|
getLogger ,
|
|
281
|
+
parseAsJSON ,
|
|
315
282
|
backOff_fetch ,
|
|
316
283
|
fetchOriginal ,
|
|
317
284
|
moveTo ,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ldn-inbox-server",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.6",
|
|
4
4
|
"description": "A demonstration Event Notifications Inbox server",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"author": "Patrick Hochstenbach <Patrick.Hochstenbach@UGent.be>",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"jsonpath": "^1.1.1",
|
|
29
29
|
"jsonschema": "^1.4.1",
|
|
30
30
|
"md5": "^2.3.0",
|
|
31
|
-
"mellon-server": "^1.0.
|
|
31
|
+
"mellon-server": "^1.0.9",
|
|
32
32
|
"node-fetch": "1.7.3",
|
|
33
33
|
"piscina": "^4.4.0",
|
|
34
34
|
"proper-lockfile": "^4.1.2",
|