ldn-inbox-server 1.3.0 → 1.3.2

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 CHANGED
@@ -33,13 +33,13 @@ curl -X POST -H 'Content-Type: application/ld+json' --data-binary '@examples/off
33
33
  Start an inbox handler with a demo handler (that creates an `Accept` message in the `./outbox`).
34
34
 
35
35
  ```
36
- npx ldn-inbox-server handle @inbox -hn ./handler/demo_notification_handler.js
36
+ npx ldn-inbox-server handler @inbox -hn ./handler/accept_notification_handler.js
37
37
  ```
38
38
 
39
- Send the notifications in the outbox:
39
+ Start an outbox handler that send the notifications that are available outbox:
40
40
 
41
41
  ```
42
- npx ldn-inbox-server handle @outbox -hn ./handler/send_notification_handler.js
42
+ npx ldn-inbox-server handler @outbox -hn ./handler/send_notification_handler.js
43
43
  ```
44
44
 
45
45
  ## Environment
@@ -62,7 +62,7 @@ npx ldn-inbox-server handle @outbox -hn ./handler/send_notification_handler.js
62
62
  Server extensions are possible by providing custom inbox and notification handlers. E.g.
63
63
 
64
64
  ```
65
- npx ldn-inbox-server handle-inbox -hn handler/demo_notification_handler.js
65
+ npx ldn-inbox-server handler @inbox -hn handler/my_handler.js
66
66
  ```
67
67
 
68
68
  Or, in JavaScript:
@@ -81,12 +81,12 @@ async function main() {
81
81
  'batch_size': 5,
82
82
  'glob': '^.*\\.jsonld$',
83
83
  'config': './config/inbox_config.json',
84
- 'notification_handler': 'handler/demo_notification_handler.js'
84
+ 'notification_handler': 'handler/my_handler.js'
85
85
  });
86
86
  }
87
87
  ```
88
88
 
89
- with `worker.js` :
89
+ with `my_handler.js` :
90
90
 
91
91
  ```
92
92
  async function handle({path,options}) {
@@ -109,10 +109,54 @@ A handler can be started on any directory. E.g. a workflow might be:
109
109
  - processed LDN messages will end up in the "outbox" box
110
110
  - invalid processing will be saved into the "error" box
111
111
 
112
- ## Multi handler
112
+ ## Handlers
113
+
114
+ ### Accept handler
115
+
116
+ A handler that creates for an incoming notification an `Accept` notification in the `@outbox`.
117
+
118
+ ### Evenlog handler
119
+
120
+ A handler that updates an event log with the incoming notification.
121
+
122
+ Requires a configuration file with property `notification_handler.eventlog`:
123
+
124
+ - `log`: the path to the event log (starting from the `public` directory)
125
+ - `dir`: the path to a container to store the events (starting from the `public` directory)
126
+
127
+ The `log` and `dir` path may contain a `@artifact(:strip)?@` directive to fill in the
128
+ path of the current artifact. The `:strip` filter is used to strip an artifact path of a file extension. E.g. `path/artifact.html` becomes `path/artifact` when using a `:strip`.
129
+
130
+ ### Multi handler
113
131
 
114
132
  A `handler/multi_notification_handler.js` is available to start multiple handler for each notification messages. The handlers to start are specified in a configuraton file that can be passed via the `config` parameter of an `handle_inbox`. In the commmand line tool `bin/ldn-inbox-server` the default location of such config file is `config/inbox_config.json` when processing an `@inbox`, and `config/outbox_config.json` when processing an `@outbox`.
115
133
 
134
+ ### Offer memento handler
135
+
136
+ A handler to `Offer` an event log to a memento server.
137
+
138
+ Requires a configuration file with property `notification_handler.offer_memento`:
139
+
140
+ - `actor`: the LDN+AS2 `actor` to use in a notification
141
+ - `target`: the LDN+AS2 `target` to use in a notification
142
+
143
+ ### Send handler
144
+
145
+ A hanlder to send notification that live in the `@outbox` via the LDN protocol to the LDN `target`.
146
+
147
+ ### Valid artifact
148
+
149
+ A handler that validates the incoming notification and checks if the `object` or `context` contains an artifact that is part of the `public` resources. See 'Artifact support' below.
150
+
151
+ ## Artifact support
152
+
153
+ This code base contains Event Notifications support for Data Node artifacts. See the examples
154
+ in `public/artifacts-example`. Move this directory tp `public/artifacts` to get a running example.
155
+
156
+ - Each artifact requires at least a `.meta` file with the `X-Artifact` header set to `true` to be recognized by the software as an artifact
157
+ - Each artifact should update the `Link-Template` header in the `.meta` file
158
+ - The config files in `config/inbox_config.json` and `config/outbox_config.json` define the location of the possible event logs for the artifact
159
+
116
160
  ## See also
117
161
 
118
162
  - [mellon-server](https://www.npmjs.com/package/mellon-server)
@@ -23,7 +23,6 @@ const HAS_PUBLIC = process.env.LDN_SERVER_HAS_PUBLIC_INBOX ?? 0;
23
23
 
24
24
  program
25
25
  .name('lnd-inbox-server')
26
- .version('1.2.2')
27
26
  .description('A demonstration Event Notifications Inbox server');
28
27
 
29
28
  program
@@ -43,6 +42,7 @@ program
43
42
 
44
43
  program
45
44
  .command('handler')
45
+ .option('--base <url>','base url',INBOX_BASE_URL)
46
46
  .option('--inbox <inbox>','inbox',INBOX_PATH)
47
47
  .option('--outbox <outbox>','outbox',OUTBOX_PATH)
48
48
  .option('--public <public>','public',PUBLIC_PATH)
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const { program } = require('commander');
5
+ const { sendNotification, getLogger } = require('../lib/util.js');
6
+ require('dotenv').config();
7
+
8
+ const logger = getLogger();
9
+
10
+ const INBOX_URL = process.env.LDN_SERVER_INBOX_URL ?? 'inbox/';
11
+ const INBOX_BASE_URL = process.env.LDN_SERVER_BASEURL ?? 'http://localhost:8000';
12
+
13
+ program
14
+ .name('post-notification')
15
+ .description('A LDN notification sender')
16
+ .argument('<url>','notification')
17
+ .argument('<file>','notification')
18
+ .action( async(url,file) => {
19
+ const to = url === '@me' ? `${INBOX_BASE_URL}/${INBOX_URL}` : url;
20
+ const json = JSON.parse(fs.readFileSync(file, { encoding: 'utf-8'}));
21
+ await sendNotification(to,json);
22
+ });
23
+
24
+ program.parse();
@@ -1,10 +1,31 @@
1
1
  {
2
2
  "notification_handler": {
3
+ "eventlog": {
4
+ "log": "@artifact:strip@.jsonld",
5
+ "dir": "@artifact:strip@"
6
+ },
3
7
  "multi": {
4
8
  "handlers": [
5
- "handler/demo_notification_handler.js" ,
6
- "handler/eventlog_notification_handler.js"
9
+ "handler/type_filter_notification_handler.js",
10
+ "handler/valid_artifact_notification_handler.js" ,
11
+ "handler/eventlog_notification_handler.js",
12
+ "handler/offer_memento_notification_handler.js"
7
13
  ]
14
+ },
15
+ "offer_memento": {
16
+ "actor": {
17
+ "id": "http://localhost:8000/profile/card#me" ,
18
+ "inbox": "http://localhost:8000/inbox/" ,
19
+ "type": "Service"
20
+ },
21
+ "target": {
22
+ "id": "http://localhost:8001/profile/card#me" ,
23
+ "inbox": "http://localhost:8001/inbox/" ,
24
+ "type": "Service"
25
+ }
26
+ },
27
+ "type_filter": {
28
+ "anyOf": [ "Offer" ]
8
29
  }
9
30
  }
10
31
  }
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "notification_handler": {
3
+ "eventlog": {
4
+ "log": "@artifact:strip@.jsonld",
5
+ "dir": "@artifact:strip@"
6
+ },
3
7
  "multi": {
4
8
  "handlers": [
9
+ "handler/valid_artifact_notification_handler.js" ,
5
10
  "handler/send_notification_handler.js",
6
11
  "handler/eventlog_notification_handler.js"
7
12
  ]
@@ -7,11 +7,11 @@
7
7
  "type": "Offer",
8
8
  "actor": {
9
9
  "id": "https://acme.org.xyz/profile/card#us",
10
- "inbox": "https://acme.org.xyz/inbox/",
10
+ "inbox": "http://localhost:8000/inbox/",
11
11
  "type": "Organization"
12
12
  },
13
13
  "object": {
14
- "id": "http://acme.org.xyz/artifacts/alice/data-set-2022-01-19.zip",
14
+ "id": "http://localhost:8000/artifacts/artifact1.html",
15
15
  "type": [ "Document" , "schema:Dataset" ]
16
16
  },
17
17
  "target": {
@@ -1,12 +1,18 @@
1
1
  const fs = require('fs');
2
2
  const md5 = require('md5');
3
+ const { parseAsJSON } = require('../lib/util');
3
4
  const logger = require('../lib/util.js').getLogger();
4
5
 
6
+ /**
7
+ * Demonstration notification handler, that creates an 'Accept'
8
+ * notification for each incoming notification message and stores
9
+ * it in the outbox container.
10
+ */
5
11
  async function handle({path,options}) {
6
12
  logger.info(`parsing notification ${path}`);
7
13
 
8
14
  try {
9
- const json = JSON.parse(fs.readFileSync(path, { encoding: 'utf-8'}));
15
+ const json = parseAsJSON(path);
10
16
 
11
17
  const id = json['id'];
12
18
  const object = json['object'];
@@ -2,37 +2,112 @@ const fs = require('fs');
2
2
  const md5 = require('md5');
3
3
  const fsPath = require('path');
4
4
  const lockfile = require('proper-lockfile');
5
+ const { parseAsJSON } = require('../lib/util');
5
6
  const logger = require('../lib/util.js').getLogger();
6
7
 
7
- const EVENT_DIR = 'events';
8
- const EVENT_LOG = 'events.jsonld';
9
-
8
+ /**
9
+ * Demonstration event log handler
10
+ */
10
11
  async function handle({path,options}) {
11
12
  logger.info(`parsing notification ${path}`);
12
-
13
+
14
+ const config = parseAsJSON(options['config']);
15
+
16
+ if (! config) {
17
+ logger.error('no configuration found for eventlog_notification_handler');
18
+ return { path, options, success: false };
19
+ }
20
+
21
+ const thisConfig = config['notification_handler']?.['eventlog'];
22
+
23
+ if (! thisConfig || !thisConfig['log'] || !thisConfig['dir']) {
24
+ logger.error('no log/dir entry for notification_handler.eventlog configuration');
25
+ return { path, options, success: false };
26
+ }
27
+
28
+ const base = options['base'] ? options['base'] :
29
+ options['host'] && options['port'] ?
30
+ `http://${options['host']}:${options['port']}` :
31
+ 'http://localhost:8000';
32
+
33
+ let eventLog = thisConfig['log'];
34
+ let eventDir = thisConfig['dir'];
35
+
36
+ let artifactPath = undefined;
37
+
38
+ if (options['artifact']) {
39
+ artifactPath = options['artifact']['id'].substring(base.length+1);
40
+ logger.info(artifactPath);
41
+ }
42
+
43
+ if (eventLog.match(/@artifact(:[a-zA-Z]+)?@/)) {
44
+ if (options['artifact']) {
45
+ if (eventLog.match(/@artifact:strip@/)) {
46
+ eventLog = eventLog.replaceAll(/@artifact(:[a-zA-Z]+)?@/g,artifactPath.replaceAll(/\.\w+$/g,''));
47
+ }
48
+ else {
49
+ eventLog = eventLog.replaceAll(/@artifact(:[a-zA-Z]+)?@/g,artifactPath);
50
+ }
51
+ }
52
+ else {
53
+ logger.error(`requested to parse ${eventLog} but no artifact information available`);
54
+ return { path, options, success: false };
55
+ }
56
+ }
57
+
58
+ if (eventDir.match(/@artifact(:[a-zA-Z]+)?@/)) {
59
+ if (options['artifact']) {
60
+ if (eventDir.match(/@artifact:strip@/)) {
61
+ eventDir = eventDir.replaceAll(/@artifact(:[a-zA-Z]+)?@/g,artifactPath.replaceAll(/\.\w+$/g,''));
62
+ }
63
+ else {
64
+ eventDir = eventDir.replaceAll(/@artifact(:[a-zA-Z]+)?@/g,artifactPath);
65
+ }
66
+ }
67
+ else {
68
+ logger.error(`requested to parse ${eventDir} but no artifact information available`);
69
+ return { path, options, success: false };
70
+ }
71
+ }
72
+
13
73
  try {
14
- const json = fs.readFileSync(path, { encoding: 'utf-8'});
74
+ const json = fs.readFileSync(path, { encoding: 'utf-8' });
15
75
 
16
76
  const fileName = path.split('/').pop();
17
- const logDir = fsPath.join(options['public'],EVENT_DIR,'log');
18
77
 
19
- if (! fs.existsSync(logDir)) {
20
- logger.info(`creating ${logDir}`);
21
- fs.mkdirSync(logDir, { recursive : true });
78
+ if (! fs.existsSync(`${options['public']}/${eventDir}`)) {
79
+ logger.info(`creating ${options['public']}/${eventDir}`);
80
+ fs.mkdirSync(`${options['public']}/${eventDir}`, { recursive : true });
22
81
  }
23
-
24
- const outboxFile = fsPath.join(logDir,fileName);
25
82
 
26
- fs.writeFileSync(outboxFile, json);
83
+ const eventFile = `${options['public']}/${eventDir}/${fileName}`;
84
+
85
+ fs.writeFileSync(eventFile, json);
27
86
 
28
87
  // Updating metadata file
29
- const metaFile = outboxFile + '.meta';
88
+ const metaFile = `${options['public']}/${eventLog}.meta`;
30
89
 
31
90
  fs.writeFileSync(metaFile, JSON.stringify({
32
91
  'Content-Type': 'application/ld+json',
33
92
  'Last-Modified': nowISO()
34
93
  },null,4));
35
94
 
95
+ // Store the path in the options .. yeah yeah we know ugly but it works for now
96
+
97
+ const eventPath = `${eventDir}/${fileName}`;
98
+ const eventId = `${base}/${eventPath}`;
99
+ const eventLogId = `${base}/${eventLog}`;
100
+
101
+ options['eventlog'] = {
102
+ 'id': eventLogId ,
103
+ 'file': `${options['public']}/${eventLog}` ,
104
+ 'dir': `${options['public']}/${eventDir}` ,
105
+ 'item': {
106
+ 'id' : eventId ,
107
+ 'file' : eventFile
108
+ }
109
+ };
110
+
36
111
  await updateEventLog({path,options});
37
112
 
38
113
  return { path, options, success: true };
@@ -51,11 +126,8 @@ async function updateEventLog({path,options}) {
51
126
  const notification = fs.readFileSync(path, { encoding: 'utf-8'});
52
127
  const notification_checksum = md5(notification);
53
128
 
54
- const baseUrl = process.env.LDN_SERVER_BASEURL ?? "";
55
- const fileName = path.split('/').pop();
56
- const entry = `${baseUrl}/${EVENT_DIR}/log/${fileName}`;
57
-
58
- const eventLog = fsPath.join(options['public'],EVENT_DIR,EVENT_LOG);
129
+ const entry = options['eventlog']['item']['id'];
130
+ const eventLog = options['eventlog']['file'];
59
131
 
60
132
  let json;
61
133
 
@@ -70,7 +142,7 @@ async function updateEventLog({path,options}) {
70
142
  };
71
143
  }
72
144
 
73
- if (json['member'].findIndex( (e) => e === entry) >= 0) {
145
+ if (json['member'].findIndex( (e) => e['id'] === entry) >= 0) {
74
146
  logger.info(`${entry} already in ${eventLog}`);
75
147
  }
76
148
  else {
@@ -1,6 +1,10 @@
1
1
  const { dynamic_handler , parseAsJSON } = require('../lib/util');
2
2
  const logger = require('../lib/util.js').getLogger();
3
3
 
4
+ /**
5
+ * Demonstration notification handler that start multiple notification handlers.
6
+ * Requires a config file that specifies which handlers to start.
7
+ */
4
8
  async function handle({path,options}) {
5
9
  let success = false;
6
10
 
@@ -0,0 +1,68 @@
1
+ const fs = require('fs');
2
+ const md5 = require('md5');
3
+ const { parseAsJSON } = require('../lib/util.js');
4
+ const logger = require('../lib/util.js').getLogger();
5
+
6
+ /**
7
+ * Demonstration notification handler, that creates an 'Offer'
8
+ * notification for each incoming notificaation to request the
9
+ * archivation of the event log.
10
+ */
11
+ async function handle({path,options}) {
12
+ logger.info(`parsing notification ${path}`);
13
+
14
+ const config = parseAsJSON(options['config']);
15
+
16
+ if (! config) {
17
+ logger.error('no configuration found for offer_memento_notification_handler');
18
+ return { path, options, success: false };
19
+ }
20
+
21
+ const thisConfig = config['notification_handler']?.['offer_memento'];
22
+
23
+ if (! thisConfig || !thisConfig['actor'] || !thisConfig['target']) {
24
+ logger.error('no actor/target entry for notification_handler.eventlog configuration');
25
+ return { path, options, success: false };
26
+ }
27
+
28
+ if (!options['artifact']) {
29
+ logger.info(`no artifact found (ignonring this request)`);
30
+ return { path, options, success: true };
31
+ }
32
+
33
+ if (!options['eventlog']) {
34
+ logger.info(`no artifact found (ignonring this request)`);
35
+ return { path, options, success: true };
36
+ }
37
+
38
+ try {
39
+ const data = JSON.stringify({
40
+ '@context': [
41
+ "https://www.w3.org/ns/activitystreams" ,
42
+ {"schema": "https://schema.org/"}
43
+ ],
44
+ type: 'Offer',
45
+ actor: thisConfig['actor'],
46
+ object: {
47
+ id: options['eventlog']['id'],
48
+ type: [ "Document", "schema:Dataset" ]
49
+ },
50
+ target: thisConfig['target']
51
+ },null,4);
52
+
53
+ const outboxFile = options['outbox'] + '/' + md5(data) + '.jsonld';
54
+
55
+ logger.info(`storing Offer to ${outboxFile}`);
56
+
57
+ fs.writeFileSync(outboxFile,data);
58
+
59
+ return { path, options, success: true };
60
+ }
61
+ catch(e) {
62
+ logger.error(`failed to process ${path}`);
63
+ logger.debug(e);
64
+ return { path, options, success: false };
65
+ }
66
+ }
67
+
68
+ module.exports = { handle };
@@ -1,11 +1,13 @@
1
- const fs = require('fs');
2
1
  const logger = require('../lib/util.js').getLogger();
3
- const { sendNotification } = require('../lib/util.js');
2
+ const { sendNotification , parseAsJSON } = require('../lib/util.js');
4
3
 
4
+ /**
5
+ * Demonstration notification handler that sends a notification to a
6
+ * target inbox.
7
+ */
5
8
  async function handle({path,options}) {
6
9
  try {
7
- const json = fs.readFileSync(path, { encoding: 'utf-8'});
8
- const data = JSON.parse(json);
10
+ const data = parseAsJSON(path);
9
11
  const type = data['type'];
10
12
  let inbox;
11
13
 
@@ -0,0 +1,65 @@
1
+ const fs = require('fs');
2
+ const { parseAsJSON } = require('../lib/util');
3
+ const logger = require('../lib/util.js').getLogger();
4
+
5
+ /**
6
+ * Demonstration notification handler, that checks if the notification
7
+ * matches a configurable list
8
+ */
9
+ async function handle({path,options}) {
10
+ logger.info(`parsing notification ${path}`);
11
+
12
+ const config = parseAsJSON(options['config']);
13
+
14
+ if (! config) {
15
+ logger.error('no configuration found for eventlog_notification_handler');
16
+ return { path, options, success: false };
17
+ }
18
+
19
+ const thisConfig = config['notification_handler']?.['type_filter'];
20
+
21
+ if (! thisConfig) {
22
+ logger.error('no notification_handler.type_filer configuration');
23
+ return { path, options, success: false };
24
+ }
25
+
26
+ if (! (thisConfig['anyOf'] && Array.isArray(thisConfig['anyOf']))) {
27
+ logger.error('no anyOf entry in notification_handler.type_filer should be an array');
28
+ return { path, options, success: false };
29
+ }
30
+
31
+ try {
32
+ const json = parseAsJSON(path);
33
+
34
+ const type = json['type'];
35
+
36
+ const typeArray = Array.isArray(type) ? type : [type];
37
+
38
+ let isOk = true ;
39
+
40
+ for (let i = 0 ; i < typeArray.length ; i++) {
41
+ if (thisConfig['anyOf'].includes(typeArray[i])) {
42
+ // We are ok
43
+ }
44
+ else {
45
+ logger.error(`${typeArray[i]} does not pass type_filter check`);
46
+ isOk = false;
47
+ break;
48
+ }
49
+ }
50
+
51
+ if (isOk) {
52
+ return { path, options, success: true };
53
+ }
54
+ else {
55
+ return { path, options, success: false };
56
+ }
57
+ }
58
+ catch(e) {
59
+ logger.error(`failed to process ${path}`);
60
+ logger.debug(e);
61
+ return { path, options, success: false };
62
+ }
63
+ }
64
+
65
+ module.exports = { handle };
@@ -0,0 +1,55 @@
1
+ const { ldPropertyAsId , parseArtifact, parseAsJSON } = require('../lib/util.js');
2
+ const logger = require('../lib/util.js').getLogger();
3
+
4
+ /**
5
+ * Demonstration notification handler, that checks if the notification
6
+ * message contains an artifact that is known to the data node
7
+ */
8
+ async function handle({path,options}) {
9
+ logger.info(`parsing notification ${path}`);
10
+
11
+ try {
12
+ const json = parseAsJSON(path);
13
+
14
+ let artifact = undefined;
15
+
16
+ if (ldPropertyAsId(json['context'])) {
17
+ artifact = ldPropertyAsId(json['context']);
18
+ }
19
+ else if (ldPropertyAsId(json['object'])) {
20
+ artifact = ldPropertyAsId(json['object']);
21
+ }
22
+ else {
23
+ logger.error(`failed to find valid context or object`);
24
+ return { path, options, success: false };
25
+ }
26
+
27
+ if (!artifact) {
28
+ logger.error(`failed to find artifact`);
29
+ return { path, options, success: false };
30
+ }
31
+
32
+ const artifactPath = parseArtifact(artifact,options);
33
+
34
+ if (artifactPath) {
35
+ // Storing the artifact path to the options.
36
+ // Maybe bad practice..but it is a workflow attribute like in Nifi :P
37
+ options['artifact'] = {
38
+ 'id': artifact ,
39
+ 'path': artifactPath
40
+ };
41
+ return { path, options, success: true };
42
+ }
43
+ else {
44
+ logger.error(`artifact ${artifact} is not known here...`);
45
+ return { path, options, success: false };
46
+ }
47
+ }
48
+ catch(e) {
49
+ logger.error(`failed to process ${path}`);
50
+ logger.debug(e);
51
+ return { path, options, success: false };
52
+ }
53
+ }
54
+
55
+ module.exports = { handle };
package/lib/util.js CHANGED
@@ -67,7 +67,7 @@ async function sendNotification(url,json,options) {
67
67
  'Accept': 'application/json',
68
68
  'Content-Type': 'application/json'
69
69
  },
70
- body: JSON.stringify(json)
70
+ body: JSON.stringify(json,null,4)
71
71
  });
72
72
 
73
73
  if (! response.ok) {
@@ -135,6 +135,60 @@ function parseAsJSON(path) {
135
135
  }
136
136
  }
137
137
 
138
+ function ldPropertyAsId(object_or_string) {
139
+ if (!object_or_string) {
140
+ return null;
141
+ }
142
+
143
+ if (typeof object_or_string === 'string' || object_or_string instanceof String) {
144
+ return object_or_string;
145
+ }
146
+ else if (typeof object_or_string === 'object' && object_or_string['id']) {
147
+ return object_or_string['id'];
148
+ }
149
+ else if (typeof object_or_string === 'object' && object_or_string['@id']) {
150
+ return object_or_string['@id'];
151
+ }
152
+ else {
153
+ return null;
154
+ }
155
+ }
156
+
157
+ function parseArtifact(url,options) {
158
+ logger.debug(`isArtifact(${url})?`);
159
+ const base = options['base'] ? options['base'] :
160
+ options['host'] && options['port'] ?
161
+ `http://${options['host']}:${options['port']}` :
162
+ 'http://localhost:8000';
163
+
164
+ const public = options['public'];
165
+
166
+ const filePath = url.substring(base.length + 1);
167
+
168
+ const metaPath = `${public}/${filePath}.meta`;
169
+
170
+ logger.debug(`searching ${metaPath}`);
171
+
172
+ if (! fs.existsSync(metaPath)) {
173
+ logger.debug(`no such file ${metaPath}`);
174
+ return null;
175
+ }
176
+
177
+ const json = parseAsJSON(metaPath);
178
+
179
+ if (json === null) {
180
+ logger.debug(`no json at ${metaPath}`);
181
+ return null;
182
+ }
183
+
184
+ if (json['X-Artifact']) {
185
+ return `${public}/${filePath}`;
186
+ }
187
+ else {
188
+ return null;
189
+ }
190
+ }
191
+
138
192
  module.exports = {
139
193
  getLogger ,
140
194
  backOff_fetch ,
@@ -142,5 +196,7 @@ module.exports = {
142
196
  moveTo ,
143
197
  sendNotification ,
144
198
  dynamic_handler ,
145
- parseAsJSON
199
+ parseAsJSON ,
200
+ parseArtifact ,
201
+ ldPropertyAsId
146
202
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ldn-inbox-server",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "A demonstration Event Notifications Inbox server",
5
5
  "main": "lib/index.js",
6
6
  "author": "Patrick Hochstenbach <Patrick.Hochstenbach@UGent.be>",
@@ -8,12 +8,12 @@
8
8
  "scripts": {
9
9
  "server": "npx ldn-inbox-server start-server",
10
10
  "demo-post": "curl -X POST -H 'Content-Type: application/ld+json' --data-binary '@examples/offer.jsonld' http://localhost:8000/inbox/",
11
- "handle-inbox": "npx ldn-inbox-server handler @inbox -hn ./handler/demo_notification_handler.js",
11
+ "handle-inbox": "npx ldn-inbox-server handler @inbox -hn ./handler/accept_notification_handler.js",
12
12
  "handle-outbox": "npx ldn-inbox-server handler @outbox -hn ./handler/send_notification_handler.js",
13
13
  "handle-eventlog": "npx ldn-inbox-server handler @inbox -hn handler/eventlog_notification_handler.js",
14
14
  "handle-inbox-multi": "npx ldn-inbox-server handler @inbox -hn ./handler/multi_notification_handler.js",
15
15
  "handle-outbox-multi": "npx ldn-inbox-server handler @outbox -hn ./handler/multi_notification_handler.js",
16
- "clean": "rm error/* inbox/* outbox/* public/events/* public/events/log/*"
16
+ "clean": "rm -rf error/* inbox/* outbox/* public/events/* public/events/log/* public/artifacts/artifact1 public/artifacts/artifact2 public/artifacts/*.jsonld*"
17
17
  },
18
18
  "bin": "./bin/ldn-inbox-server.js",
19
19
  "keywords": [
@@ -31,6 +31,7 @@
31
31
  "node-fetch": "1.7.3",
32
32
  "piscina": "^4.4.0",
33
33
  "proper-lockfile": "^4.1.2",
34
+ "upath": "^2.0.1",
34
35
  "uuid": "^9.0.1"
35
36
  }
36
37
  }
@@ -0,0 +1,17 @@
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>Artifact 1</title>
8
+ </head>
9
+ <body>
10
+ <main class="container">
11
+ <h1>Artifact 1</h1>
12
+ <p>
13
+ <a href="artifact1.jsonld">Event Log</a>
14
+ </p>
15
+ </main>
16
+ </body>
17
+ </html>
@@ -0,0 +1,6 @@
1
+ {
2
+ "Content-Type": "text/html",
3
+ "X-Artifact": true,
4
+ "Link-Template": "\"https://{YOUR-SERVER}/artifacts/artifact1.jsonld\"; rel=\"eventlog\"",
5
+ "Link": "</inbox/> ; rel=\"http://www.w3.org/ns/ldp#inbox\""
6
+ }
@@ -0,0 +1,17 @@
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>Artifact 2</title>
8
+ </head>
9
+ <body>
10
+ <main class="container">
11
+ <h1>Artifact 2</h1>
12
+ <p>
13
+ <a href="artifact1.jsonld">Event Log</a>
14
+ </p>
15
+ </main>
16
+ </body>
17
+ </html>
@@ -0,0 +1,6 @@
1
+ {
2
+ "Content-Type": "text/html",
3
+ "X-Artifact": true,
4
+ "Link-Template": "\"https://{YOUR-SERVER}/artifacts/artifact2.jsonld\"; rel=\"eventlog\"",
5
+ "Link": "</inbox/> ; rel=\"http://www.w3.org/ns/ldp#inbox\""
6
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "Content-Type": "text/html"
3
+ }