@jrpool/kilotest 35.0.1 → 35.1.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/SERVICE.md CHANGED
@@ -20,6 +20,8 @@ Any other application `xyz` can be installed at `/opt/jpdev/xyz` on the server.
20
20
 
21
21
  ## Process management
22
22
 
23
+ ### Process manager
24
+
23
25
  Kilotest is managed with [PM2](https://pm2.keymetrics.io) on the server (not on the local development host). The PM2 configuration is specified in the repository as `pm2.config.js`:
24
26
 
25
27
  ```javascript
@@ -49,6 +51,10 @@ pm2 restart kilotest --time --update-env
49
51
  pm2 save
50
52
  ```
51
53
 
54
+ The management of the PM2 logs is [documented by PM2](https://pm2.keymetrics.io/docs/usage/log-management/).
55
+
56
+ ### Process tuning
57
+
52
58
  The server configuration has been tuned for improved performance. The file edited for this purpose is `/etc/default/zramswap`:
53
59
 
54
60
  ```properties
@@ -47,7 +47,7 @@ exports.answer = async (_, search) => {
47
47
  // Report the error.
48
48
  return {
49
49
  status: 'error',
50
- error: 'Invalid authorization code'
50
+ message: 'Invalid authorization code'
51
51
  }
52
52
  }
53
53
  }
@@ -60,7 +60,7 @@ exports.answer = async (_, search) => {
60
60
  oldBalance = `$${balanceData.balance} is the`;
61
61
  }
62
62
  // If the balance file does not exist or is invalid:
63
- catch (error) {
63
+ catch {
64
64
  oldBalance = 'There is no';
65
65
  }
66
66
  }
@@ -135,6 +135,6 @@ exports.answer = async (pageArgs, search) => {
135
135
  // Otherwise, report this.
136
136
  return {
137
137
  status: 'error',
138
- error: 'Invalid report specification'
138
+ message: 'Invalid report specification'
139
139
  };
140
140
  };
package/index.js CHANGED
@@ -80,7 +80,8 @@ const AI_MODEL0_OUTPUT_PRICE = Number(process.env.AI_MODEL0_OUTPUT_PRICE);
80
80
 
81
81
  // Serves or sends an error message.
82
82
  const serveError = async (error, response, isHumanUser = true) => {
83
- console.log(error.message || 'ERROR');
83
+ const errorLines = Object.entries(error).map(pair => `${pair[0]}: ${pair[1]}`);
84
+ console.log(errorLines.join('\n') || 'ERROR');
84
85
  if (! response.writableEnded) {
85
86
  response.statusCode = 400;
86
87
  // If the request is from a human user:
@@ -128,7 +129,7 @@ const checkBalancesForAlerts = async report => {
128
129
  // Get the recorded AI service 0 balance.
129
130
  balanceJSON = await fs.readFile(balancePath, 'utf8');
130
131
  }
131
- catch (error) {
132
+ catch {
132
133
  console.error('ERROR: AI service 0 balance file missing');
133
134
  }
134
135
  // If the variables required for an AI service 0 balance alert are defined:
@@ -169,6 +170,21 @@ const checkBalancesForAlerts = async report => {
169
170
  }
170
171
  }
171
172
  };
173
+ // Creates an error object about a suspicious request.
174
+ const getAbuseError = (request, reason) => {
175
+ const {method, url, headers} = request;
176
+ const ip = headers['x-forwarded-for'] || request.socket.remoteAddress || 'unknown';
177
+ return {
178
+ message: 'Invalid request',
179
+ reason,
180
+ 'IP address': ip,
181
+ method,
182
+ URL: url,
183
+ 'user agent': headers['user-agent'] || 'none',
184
+ referer: headers.referer || 'none',
185
+ time: new Date().toISOString()
186
+ };
187
+ };
172
188
  // Handles a request.
173
189
  const requestHandler = async (request, response) => {
174
190
  // Sets response headers.
@@ -260,17 +276,17 @@ const requestHandler = async (request, response) => {
260
276
  // If the report exists and is hidden:
261
277
  if (reportHidden === true) {
262
278
  console.log(`ERROR: Hidden report ${timeStamp}-${jobID} requested`);
263
- // Report this.
279
+ // Report this as suspected abuse.
264
280
  await serveError(
265
- {message: `ERROR: requested report ${timeStamp}-${jobID} not available`},
281
+ getAbuseError(request, `Requested report ${timeStamp}-${jobID} not available`),
266
282
  response,
267
283
  true
268
284
  );
269
285
  }
270
- // Otherwise, if any other error occurred:
286
+ // Otherwise, if the report does not exist or another error occurred:
271
287
  else if (typeof reportHidden === 'string') {
272
- // Report it.
273
- await serveError({message: reportHidden}, response, true);
288
+ // Report this as suspected abuse.
289
+ await serveError(getAbuseError(request, reportHidden), response, true);
274
290
  }
275
291
  // Otherwise, i.e. if the report log is valid and not hidden:
276
292
  else {
@@ -314,14 +330,14 @@ const requestHandler = async (request, response) => {
314
330
  }
315
331
  // Otherwise, i.e. if they are invalid:
316
332
  else {
317
- // Report the error.
318
- await serveError({message: answerData.error}, response, true);
333
+ // Report the error as suspected abuse.
334
+ await serveError(getAbuseError(request, answerData.message), response, true);
319
335
  }
320
336
  }
321
337
  // Otherwise, i.e. if the answer cannot be generated:
322
338
  else {
323
- // Report the error.
324
- await serveError({message: 'ERROR: Invalid request'}, response, true);
339
+ // Report the error as suspected abuse.
340
+ await serveError(getAbuseError(request, 'Request for nonexistent page'), response, true);
325
341
  }
326
342
  }
327
343
  // Otherwise, if it is for an API service:
@@ -377,7 +393,7 @@ const requestHandler = async (request, response) => {
377
393
  setHeaders(mimeTypes[ext] || 'application/octet-stream', null, 'low');
378
394
  response.end(img);
379
395
  }
380
- catch (_) {
396
+ catch {
381
397
  await serveError({message: 'ERROR: Image not found'}, response, true);
382
398
  }
383
399
  }
@@ -439,7 +455,7 @@ const requestHandler = async (request, response) => {
439
455
  // Otherwise, i.e. if they are invalid:
440
456
  else {
441
457
  // Report the error.
442
- await serveError({message: answerData.error}, response, true);
458
+ await serveError({message: answerData.message}, response, true);
443
459
  }
444
460
  }
445
461
  // Otherwise, i.e. if the request is invalid:
@@ -473,7 +489,7 @@ const requestHandler = async (request, response) => {
473
489
  // Otherwise, i.e. if they are invalid:
474
490
  else {
475
491
  // Report the error.
476
- await serveError({message: answerData.error}, response, true);
492
+ await serveError({message: answerData.message}, response, true);
477
493
  }
478
494
  }
479
495
  }
@@ -506,7 +522,7 @@ const requestHandler = async (request, response) => {
506
522
  // Otherwise, i.e. if they are invalid:
507
523
  else {
508
524
  // Report the error.
509
- await serveError({message: answerData.error}, response, true);
525
+ await serveError({message: answerData.message}, response, true);
510
526
  }
511
527
  }
512
528
  // Otherwise, i.e. if it is a rejection:
@@ -547,7 +563,7 @@ const requestHandler = async (request, response) => {
547
563
  // Otherwise, i.e. if they are invalid:
548
564
  else {
549
565
  // Report the error.
550
- await serveError({message: answerData.error}, response, true);
566
+ await serveError({message: answerData.message}, response, true);
551
567
  }
552
568
  }
553
569
  // Otherwise, if it is a WCAG map renewal:
@@ -566,7 +582,7 @@ const requestHandler = async (request, response) => {
566
582
  // Otherwise, i.e. if they are invalid:
567
583
  else {
568
584
  // Report the error.
569
- await serveError({message: answerData.error}, response, true);
585
+ await serveError({message: answerData.message}, response, true);
570
586
  }
571
587
  }
572
588
  // Otherwise, if it is a request from an agent:
@@ -753,7 +769,7 @@ const requestHandler = async (request, response) => {
753
769
  }
754
770
  else {
755
771
  response.statusCode = 400;
756
- response.end(JSON.stringify({status: 'error', message: answerData.error}));
772
+ response.end(JSON.stringify({status: 'error', message: answerData.message}));
757
773
  }
758
774
  }
759
775
  // Otherwise, i.e. if it is any other POST request:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jrpool/kilotest",
3
- "version": "35.0.1",
3
+ "version": "35.1.0",
4
4
  "description": "An ensemble testing service with a focus on accessibility",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -34,6 +34,6 @@ exports.answer = async authCode => {
34
34
  // Otherwise, i.e. if the authorization code is invalid, return an error page.
35
35
  return {
36
36
  status: 'error',
37
- error: 'Invalid authorization code'
37
+ message: 'Invalid authorization code'
38
38
  };
39
39
  };
@@ -32,7 +32,7 @@ exports.answer = async (_, search) => {
32
32
  // Report the error.
33
33
  return {
34
34
  status: 'error',
35
- error: 'Invalid authorization code'
35
+ message: 'Invalid authorization code'
36
36
  }
37
37
  }
38
38
  }
@@ -176,6 +176,6 @@ exports.answer = async pageArgs => {
176
176
  // Otherwise, report this.
177
177
  return {
178
178
  status: 'error',
179
- error: 'Invalid report specification'
179
+ message: 'Invalid report specification'
180
180
  };
181
181
  };
@@ -171,6 +171,6 @@ exports.answer = async pageArgs => {
171
171
  // Otherwise, i.e. if they were not obtained, report this.
172
172
  return {
173
173
  status: 'error',
174
- error: 'Report processing failed'
174
+ message: 'Report processing failed'
175
175
  };
176
176
  };
@@ -32,7 +32,7 @@ exports.answer = async (_, search) => {
32
32
  // Report the error.
33
33
  return {
34
34
  status: 'error',
35
- error: 'Invalid authorization code'
35
+ message: 'Invalid authorization code'
36
36
  }
37
37
  }
38
38
  }
@@ -33,7 +33,7 @@ exports.answer = async (_, search) => {
33
33
  // Report the error.
34
34
  return {
35
35
  status: 'error',
36
- error: 'Invalid authorization code'
36
+ message: 'Invalid authorization code'
37
37
  }
38
38
  }
39
39
  }
@@ -33,7 +33,7 @@ exports.answer = async (_, search) => {
33
33
  // Report the error.
34
34
  return {
35
35
  status: 'error',
36
- error: 'Invalid authorization code'
36
+ message: 'Invalid authorization code'
37
37
  }
38
38
  }
39
39
  }
@@ -33,7 +33,7 @@ exports.answer = async (_, search) => {
33
33
  // Report the error.
34
34
  return {
35
35
  status: 'error',
36
- error: 'Invalid authorization code'
36
+ message: 'Invalid authorization code'
37
37
  }
38
38
  }
39
39
  }
package/researchAgent.js CHANGED
@@ -63,7 +63,7 @@ const getContent = async response => {
63
63
  // If it is not JSON:
64
64
  catch {
65
65
  // Return this.
66
- resolve({error: `Response content not JSON (${contentString})`});
66
+ resolve({message: `Response content not JSON (${contentString})`});
67
67
  }
68
68
  });
69
69
  });
package/style.css CHANGED
@@ -57,7 +57,7 @@ code.thin {
57
57
  margin-top: 0;
58
58
  }
59
59
  details {
60
- padding: rem;
60
+ padding: 0.5rem;
61
61
  border: solid #000 1px;
62
62
  }
63
63
  .error {
package/targets/index.js CHANGED
@@ -12,7 +12,6 @@ const {
12
12
  getObject,
13
13
  getPageDataStrings,
14
14
  getRecs,
15
- getToolNamesString,
16
15
  getLogs,
17
16
  getReportData,
18
17
  isRecommendable,
@@ -57,6 +57,6 @@ exports.answer = async (url, what, authCode) => {
57
57
  // Otherwise, i.e. if the authorization code is invalid, return an error page.
58
58
  return {
59
59
  status: 'error',
60
- error: 'Invalid authorization code'
60
+ message: 'Invalid authorization code'
61
61
  };
62
62
  };
package/testRec/index.js CHANGED
@@ -17,7 +17,7 @@ exports.answer = async (what, url, why) => {
17
17
  // Return an answer reporting this.
18
18
  return {
19
19
  status: 'error',
20
- error: `Page is already ${status}`
20
+ message: `Page is already ${status}`
21
21
  };
22
22
  }
23
23
  // Otherwise, i.e. if it is recommendable, process the recommendation.
package/tutorial/index.js CHANGED
@@ -33,11 +33,11 @@ exports.answer = async () => {
33
33
  // Sanitizes and saves a tutorial comment to comments.json.
34
34
  exports.saveComment = async content => {
35
35
  if (!content || typeof content !== 'string') {
36
- return {status: 'error', error: 'No content provided'};
36
+ return {status: 'error', message: 'No content provided'};
37
37
  }
38
38
  const sanitized = sanitize(content);
39
39
  if (!sanitized) {
40
- return {status: 'error', error: 'Comment is empty after sanitization'};
40
+ return {status: 'error', message: 'Comment is empty after sanitization'};
41
41
  }
42
42
  let comments = [];
43
43
  try {
package/util.js CHANGED
@@ -679,7 +679,7 @@ exports.processRec = async (testType, dirName, what, url, why) => {
679
679
  }
680
680
  return {
681
681
  status: 'error',
682
- error: 'Invalid recommendation'
682
+ message: 'Invalid recommendation'
683
683
  };
684
684
  };
685
685
  // Gets the WCAG Understanding link for a numeric WCAG standard identifier.
@@ -50,21 +50,21 @@ exports.answer = async authCode => {
50
50
  // Report this.
51
51
  return {
52
52
  status: 'error',
53
- error: 'No entries found in WCAG map source'
53
+ message: 'No entries found in WCAG map source'
54
54
  };
55
55
  }
56
56
  }
57
57
  else {
58
58
  return {
59
59
  status: 'error',
60
- error: 'WCAG map source not retrieved'
60
+ message: 'WCAG map source not retrieved'
61
61
  };
62
62
  }
63
63
  }
64
64
  else {
65
65
  return {
66
66
  status: 'error',
67
- error: 'Authorization code invalid'
67
+ message: 'Authorization code invalid'
68
68
  };
69
69
  }
70
70
  };