ldn-inbox-server 1.4.5 → 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 +2 -2
- package/lib/index.js +40 -75
- 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,12 +11,12 @@ 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
|
|
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');
|
|
@@ -13,51 +13,15 @@ const {
|
|
|
13
13
|
const { handle_inbox } = require('../lib/handler');
|
|
14
14
|
const logger = getLogger();
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
let INBOX_PUBLIC_READABLE = 0;
|
|
20
|
-
let JSON_SCHEMA = '';
|
|
21
|
-
|
|
22
|
-
function inbox_server(options) {
|
|
23
|
-
INBOX_URL = options['url'];
|
|
24
|
-
INBOX_PATH = options['inbox'];
|
|
25
|
-
INBOX_BASE_URL = options['base'];
|
|
26
|
-
INBOX_PUBLIC_READABLE = options['inboxPublic'];
|
|
27
|
-
JSON_SCHEMA = JSON.parse(fs.readFileSync(options['schema'], { encoding: 'utf-8'}));
|
|
28
|
-
let registry = [{ path : `${INBOX_URL}.*` , do: doInbox }];
|
|
29
|
-
|
|
30
|
-
// Add other (external defined) registry parts
|
|
31
|
-
if (options['registry']) {
|
|
32
|
-
const path = options['registry'];
|
|
33
|
-
let registry2;
|
|
34
|
-
if (typeof path === 'string' || path instanceof String) {
|
|
35
|
-
registry2 = JSON.parse(fs.readFileSync(path,{ encoding: 'utf-8'}));
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
registry2 = path;
|
|
39
|
-
}
|
|
40
|
-
registry = registry.concat(registry2);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
start_server({
|
|
44
|
-
host: options['host'],
|
|
45
|
-
port: options['port'],
|
|
46
|
-
base: options['base'],
|
|
47
|
-
public: options['public'],
|
|
48
|
-
registry: registry
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function doInbox(req,res) {
|
|
53
|
-
if (req.method === 'GET' && INBOX_PUBLIC_READABLE == 1) {
|
|
54
|
-
doInboxGET(req,res);
|
|
16
|
+
function doInbox(req,res,opts) {
|
|
17
|
+
if (req.method === 'GET' && opts['inboxPublic'] == 1) {
|
|
18
|
+
doInboxGET(req,res,opts);
|
|
55
19
|
}
|
|
56
|
-
else if (req.method == 'HEAD' &&
|
|
57
|
-
doInboxHEAD(req,res);
|
|
20
|
+
else if (req.method == 'HEAD' && opts['inboxPublic'] == 1) {
|
|
21
|
+
doInboxHEAD(req,res,opts);
|
|
58
22
|
}
|
|
59
|
-
else if (req.method === 'POST') {
|
|
60
|
-
doInboxPOST(req,res);
|
|
23
|
+
else if (req.method === 'POST' && opts['inboxWriteable'] == 1) {
|
|
24
|
+
doInboxPOST(req,res,opts);
|
|
61
25
|
}
|
|
62
26
|
else {
|
|
63
27
|
logger.error(`tried method ${req.method} on inbox : forbidden`);
|
|
@@ -66,16 +30,16 @@ function doInbox(req,res) {
|
|
|
66
30
|
}
|
|
67
31
|
}
|
|
68
32
|
|
|
69
|
-
function doInboxGET(req,res) {
|
|
70
|
-
const pathItem = req.url.substring(
|
|
33
|
+
function doInboxGET(req,res,opts) {
|
|
34
|
+
const pathItem = req.url.substring(opts['url'].length);
|
|
71
35
|
|
|
72
36
|
logger.debug(`doInboxGET (for ${pathItem})`);
|
|
73
37
|
|
|
74
38
|
if (pathItem === '/') {
|
|
75
|
-
doInboxGET_Index(req,res);
|
|
39
|
+
doInboxGET_Index(req,res,opts);
|
|
76
40
|
}
|
|
77
41
|
else if (pathItem.match(/^\/[A-Za-z0-9_-]+\.jsonld$/)) {
|
|
78
|
-
doInboxGET_Read(req,res);
|
|
42
|
+
doInboxGET_Read(req,res,opts);
|
|
79
43
|
}
|
|
80
44
|
else {
|
|
81
45
|
res.writeHead(403);
|
|
@@ -83,14 +47,14 @@ function doInboxGET(req,res) {
|
|
|
83
47
|
}
|
|
84
48
|
}
|
|
85
49
|
|
|
86
|
-
function doInboxHEAD(req,res) {
|
|
87
|
-
const pathItem = req.url.substring(
|
|
50
|
+
function doInboxHEAD(req,res,opts) {
|
|
51
|
+
const pathItem = req.url.substring(opts['url'].length);
|
|
88
52
|
const id = pathItem.substring(1);
|
|
89
53
|
|
|
90
54
|
logger.debug(`doInboxHEAD (for ${pathItem})`);
|
|
91
55
|
|
|
92
56
|
if (pathItem === '/') {
|
|
93
|
-
const meta = getBody(`${id}.meta
|
|
57
|
+
const meta = getBody(`${id}.meta`,opts);
|
|
94
58
|
|
|
95
59
|
if (meta) {
|
|
96
60
|
const metadata = JSON.parse(meta);
|
|
@@ -107,7 +71,7 @@ function doInboxHEAD(req,res) {
|
|
|
107
71
|
}
|
|
108
72
|
|
|
109
73
|
if (pathItem.match(/^\/[A-Za-z0-9_-]+\.jsonld$/)) {
|
|
110
|
-
const result = getBody(id);
|
|
74
|
+
const result = getBody(id,opts);
|
|
111
75
|
|
|
112
76
|
if (! result) {
|
|
113
77
|
res.writeHead(403);
|
|
@@ -115,7 +79,7 @@ function doInboxHEAD(req,res) {
|
|
|
115
79
|
return;
|
|
116
80
|
}
|
|
117
81
|
|
|
118
|
-
const meta = getBody(`${id}.meta
|
|
82
|
+
const meta = getBody(`${id}.meta`,opts);
|
|
119
83
|
|
|
120
84
|
if (meta) {
|
|
121
85
|
const metadata = JSON.parse(meta);
|
|
@@ -138,18 +102,18 @@ function doInboxHEAD(req,res) {
|
|
|
138
102
|
}
|
|
139
103
|
}
|
|
140
104
|
|
|
141
|
-
function doInboxGET_Index(req,res) {
|
|
142
|
-
const notifications = listInbox().map( (e) => {
|
|
143
|
-
return
|
|
105
|
+
function doInboxGET_Index(req,res,opts) {
|
|
106
|
+
const notifications = listInbox(opts).map( (e) => {
|
|
107
|
+
return opts['base'] + '/' + opts['url'] + e;
|
|
144
108
|
});
|
|
145
109
|
|
|
146
110
|
const result = {
|
|
147
111
|
"@context": "http://www.w3.org/ns/ldp",
|
|
148
|
-
"@id":
|
|
112
|
+
"@id": opts['base'] + '/' + opts['url'],
|
|
149
113
|
"contains": notifications
|
|
150
114
|
};
|
|
151
115
|
|
|
152
|
-
const meta = getBody(`.meta
|
|
116
|
+
const meta = getBody(`.meta`,opts);
|
|
153
117
|
|
|
154
118
|
if (meta) {
|
|
155
119
|
const metadata = JSON.parse(meta);
|
|
@@ -165,13 +129,13 @@ function doInboxGET_Index(req,res) {
|
|
|
165
129
|
res.end(JSON.stringify(result,null,2));
|
|
166
130
|
}
|
|
167
131
|
|
|
168
|
-
function doInboxGET_Read(req,res) {
|
|
169
|
-
const pathItem = req.url.substring(
|
|
132
|
+
function doInboxGET_Read(req,res,opts) {
|
|
133
|
+
const pathItem = req.url.substring(opts['url'].length);
|
|
170
134
|
const id = pathItem.substring(1);
|
|
171
|
-
const result = getBody(id);
|
|
135
|
+
const result = getBody(id,opts);
|
|
172
136
|
|
|
173
137
|
if (result) {
|
|
174
|
-
const meta = getBody(`${id}.meta
|
|
138
|
+
const meta = getBody(`${id}.meta`,opts);
|
|
175
139
|
|
|
176
140
|
if (meta) {
|
|
177
141
|
const metadata = JSON.parse(meta);
|
|
@@ -192,8 +156,8 @@ function doInboxGET_Read(req,res) {
|
|
|
192
156
|
}
|
|
193
157
|
}
|
|
194
158
|
|
|
195
|
-
function doInboxPOST(req,res) {
|
|
196
|
-
const pathItem = req.url.substring(
|
|
159
|
+
function doInboxPOST(req,res,opts) {
|
|
160
|
+
const pathItem = req.url.substring(opts['url'].length);
|
|
197
161
|
|
|
198
162
|
if (pathItem !== '/') {
|
|
199
163
|
req.writeHead(403);
|
|
@@ -223,8 +187,8 @@ function doInboxPOST(req,res) {
|
|
|
223
187
|
});
|
|
224
188
|
req.on('end',() => {
|
|
225
189
|
logger.debug(postData);
|
|
226
|
-
if (checkBody(postData)) {
|
|
227
|
-
const id = storeBody(postData);
|
|
190
|
+
if (checkBody(postData,opts)) {
|
|
191
|
+
const id = storeBody(postData,opts);
|
|
228
192
|
logger.debug(`accepted ${req.url}${id}`);
|
|
229
193
|
res.setHeader('Location',`${req.url}${id}`);
|
|
230
194
|
res.writeHead(201);
|
|
@@ -238,13 +202,13 @@ function doInboxPOST(req,res) {
|
|
|
238
202
|
});
|
|
239
203
|
}
|
|
240
204
|
|
|
241
|
-
function listInbox() {
|
|
205
|
+
function listInbox(opts) {
|
|
242
206
|
const glob = new RegExp("^.*\\.jsonld$");
|
|
243
207
|
|
|
244
208
|
logger.debug(`listInbox()`);
|
|
245
209
|
|
|
246
210
|
try {
|
|
247
|
-
const entries = fs.readdirSync(
|
|
211
|
+
const entries = fs.readdirSync(opts['inbox']).filter( (file) => {
|
|
248
212
|
return file.match(glob);
|
|
249
213
|
});
|
|
250
214
|
return entries;
|
|
@@ -255,21 +219,21 @@ function listInbox() {
|
|
|
255
219
|
}
|
|
256
220
|
}
|
|
257
221
|
|
|
258
|
-
function getBody(id) {
|
|
222
|
+
function getBody(id,opts) {
|
|
259
223
|
logger.debug(`getBody(${id})`);
|
|
260
224
|
|
|
261
225
|
try {
|
|
262
|
-
return fs.readFileSync(
|
|
226
|
+
return fs.readFileSync(opts['inbox'] + '/' + id, {encoding : 'utf-8'});
|
|
263
227
|
}
|
|
264
228
|
catch(e) {
|
|
265
229
|
return null;
|
|
266
230
|
}
|
|
267
231
|
}
|
|
268
232
|
|
|
269
|
-
function storeBody(data) {
|
|
233
|
+
function storeBody(data,opts) {
|
|
270
234
|
try {
|
|
271
235
|
const id = md5(data);
|
|
272
|
-
const newpath = `${
|
|
236
|
+
const newpath = `${opts['inbox']}/${id}.jsonld`;
|
|
273
237
|
|
|
274
238
|
if (! fs.existsSync(newpath)) {
|
|
275
239
|
logger.debug(`storing ${newpath}`);
|
|
@@ -286,12 +250,13 @@ function storeBody(data) {
|
|
|
286
250
|
}
|
|
287
251
|
}
|
|
288
252
|
|
|
289
|
-
function checkBody(data) {
|
|
290
|
-
if (!
|
|
253
|
+
function checkBody(data,opts) {
|
|
254
|
+
if (! (opts['schema'] && fs.existsSync(opts['schema']))) {
|
|
291
255
|
logger.debug(`no JSON_SCHEMA found`);
|
|
292
256
|
return true;
|
|
293
257
|
}
|
|
294
258
|
try {
|
|
259
|
+
const JSON_SCHEMA = JSON.parse(fs.readFileSync(opts['schema'], { encoding: 'utf-8'}));
|
|
295
260
|
const json = JSON.parse(data);
|
|
296
261
|
const v = new Validator();
|
|
297
262
|
const res = v.validate(json,JSON_SCHEMA);
|
|
@@ -311,7 +276,7 @@ function checkBody(data) {
|
|
|
311
276
|
}
|
|
312
277
|
|
|
313
278
|
module.exports = {
|
|
314
|
-
|
|
279
|
+
doInbox,
|
|
315
280
|
getLogger ,
|
|
316
281
|
parseAsJSON ,
|
|
317
282
|
backOff_fetch ,
|
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",
|