@jrpool/kilotest 31.2.2 → 33.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/AI-TOOL.md +82 -20
- package/IDEAS.md +1 -1
- package/README.md +1 -1
- package/SERVICE.md +1 -1
- package/diagnoses/index.html +1 -1
- package/index.html +12 -12
- package/index.js +286 -286
- package/llms-full.txt +39 -30
- package/mcp.js +96 -0
- package/openapi.yaml +38 -38
- package/package.json +4 -2
- package/pm2.config.js +14 -12
- package/reportIssue/index.js +1 -1
- package/reportIssues/api.js +12 -10
- package/reportIssues/index.js +2 -2
- package/targets/api.js +7 -6
- package/testRecForm/api.js +4 -3
- package/tutorial/index.html +1 -1
- package/util.js +16 -7
package/index.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
// ENVIRONMENT
|
|
7
7
|
|
|
8
|
-
// Module to keep secrets local.
|
|
9
8
|
require('dotenv').config({quiet: true});
|
|
10
9
|
|
|
11
10
|
// IMPORTS
|
|
@@ -14,13 +13,13 @@ const {
|
|
|
14
13
|
getJobNames,
|
|
15
14
|
getJSON,
|
|
16
15
|
getLogPath,
|
|
17
|
-
getLogs,
|
|
18
16
|
getObject,
|
|
19
17
|
getPOSTData,
|
|
20
18
|
getReport,
|
|
21
19
|
getRecs,
|
|
22
20
|
getReportPath,
|
|
23
21
|
isHidden,
|
|
22
|
+
isReportAvailable,
|
|
24
23
|
isTimeStamp,
|
|
25
24
|
isJobID,
|
|
26
25
|
isURL,
|
|
@@ -29,6 +28,7 @@ const {
|
|
|
29
28
|
reportsPath,
|
|
30
29
|
ruleIDs
|
|
31
30
|
} = require('./util');
|
|
31
|
+
const {handleMCP} = require('./mcp');
|
|
32
32
|
const fs = require('fs/promises');
|
|
33
33
|
const http = require('http');
|
|
34
34
|
const https = require('https');
|
|
@@ -171,17 +171,6 @@ const checkBalancesForAlerts = async report => {
|
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
};
|
|
174
|
-
// Minifies a URL.
|
|
175
|
-
const minifyURL = url => url.replace(/www\.|\/$/g, '');
|
|
176
|
-
// Returns whether a report on a page is available.
|
|
177
|
-
const isReportAvailable = async (what, url) => {
|
|
178
|
-
const logs = await getLogs();
|
|
179
|
-
const whats = logs.map(log => log.what);
|
|
180
|
-
const urls = logs.map(log => log.url);
|
|
181
|
-
const miniURLs = urls.map(url => minifyURL(url));
|
|
182
|
-
const miniURL = minifyURL(url);
|
|
183
|
-
return whats.includes(what) || miniURLs.includes(miniURL);
|
|
184
|
-
};
|
|
185
174
|
// Handles a request.
|
|
186
175
|
const requestHandler = async (request, response) => {
|
|
187
176
|
// Sets response headers.
|
|
@@ -218,8 +207,12 @@ const requestHandler = async (request, response) => {
|
|
|
218
207
|
}
|
|
219
208
|
// Otherwise, if the request is a GET request:
|
|
220
209
|
else if (method === 'GET') {
|
|
221
|
-
// If it is for the
|
|
222
|
-
if (
|
|
210
|
+
// If it is for the model context protocol server:
|
|
211
|
+
if (pathname === '/mcp') {
|
|
212
|
+
await handleMCP(request, response);
|
|
213
|
+
}
|
|
214
|
+
// Otherwise, if it is for the home page:
|
|
215
|
+
else if (['/', '/index.html'].includes(pathname)) {
|
|
223
216
|
// Get the home page.
|
|
224
217
|
const homePage = await fs.readFile('index.html', 'utf8');
|
|
225
218
|
// Serve it.
|
|
@@ -313,7 +306,7 @@ const requestHandler = async (request, response) => {
|
|
|
313
306
|
const topic = pageName.slice(0, -5);
|
|
314
307
|
// If the page can be generated:
|
|
315
308
|
if (answer[topic]) {
|
|
316
|
-
setHeaders('text/html', `${pathname}${search}`, '
|
|
309
|
+
setHeaders('text/html', `${pathname}${search}`, 'ultra');
|
|
317
310
|
// Get the answer data.
|
|
318
311
|
const answerData = await answer[topic](pathTail, search);
|
|
319
312
|
// If they are valid:
|
|
@@ -427,53 +420,25 @@ const requestHandler = async (request, response) => {
|
|
|
427
420
|
}
|
|
428
421
|
// Otherwise, if the request is a POST request:
|
|
429
422
|
else if (method === 'POST') {
|
|
430
|
-
//
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
if (pageName === 'retestRec.html') {
|
|
434
|
-
const {why} = postData;
|
|
435
|
-
const [timeStamp, jobID] = pathTail.split('/');
|
|
436
|
-
// If the request is valid:
|
|
437
|
-
if (isTimeStamp(timeStamp) && isJobID(jobID) && why) {
|
|
438
|
-
// Serve response headers.
|
|
439
|
-
setHeaders('text/html', `${pathname}${search}`, 'ultra');
|
|
440
|
-
// Get the answer data.
|
|
441
|
-
const answerData = await require(path.join(__dirname, 'retestRec', 'index'))
|
|
442
|
-
.answer(pathTail, why);
|
|
443
|
-
// If they are valid:
|
|
444
|
-
if (answerData.status === 'ok') {
|
|
445
|
-
// Serve the answer page.
|
|
446
|
-
response.end(answerData.answerPage);
|
|
447
|
-
}
|
|
448
|
-
// Otherwise, i.e. if they are invalid:
|
|
449
|
-
else {
|
|
450
|
-
// Report the error.
|
|
451
|
-
await serveError({message: answerData.error}, response, true);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
// Otherwise, i.e. if the request is invalid:
|
|
455
|
-
else {
|
|
456
|
-
// Report the error.
|
|
457
|
-
await serveError({message: 'ERROR: Invalid retest recommendation'}, response, true);
|
|
458
|
-
}
|
|
423
|
+
// If it is for the model context protocol server:
|
|
424
|
+
if (pageName === 'mcp') {
|
|
425
|
+
await handleMCP(request, response);
|
|
459
426
|
}
|
|
460
|
-
// Otherwise, if it is
|
|
461
|
-
else
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
else {
|
|
472
|
-
// Serve headers for a response.
|
|
427
|
+
// Otherwise, i.e. if it is not for the MCP server:
|
|
428
|
+
else {
|
|
429
|
+
// Get the data from the request body.
|
|
430
|
+
const postData = await getPOSTData(request);
|
|
431
|
+
// If the request is a retest recommendation:
|
|
432
|
+
if (pageName === 'retestRec.html') {
|
|
433
|
+
const {why} = postData;
|
|
434
|
+
const [timeStamp, jobID] = pathTail.split('/');
|
|
435
|
+
// If the request is valid:
|
|
436
|
+
if (isTimeStamp(timeStamp) && isJobID(jobID) && why) {
|
|
437
|
+
// Serve response headers.
|
|
473
438
|
setHeaders('text/html', `${pathname}${search}`, 'ultra');
|
|
474
439
|
// Get the answer data.
|
|
475
|
-
const answerData = await require(path.join(__dirname, '
|
|
476
|
-
.answer(
|
|
440
|
+
const answerData = await require(path.join(__dirname, 'retestRec', 'index'))
|
|
441
|
+
.answer(pathTail, why);
|
|
477
442
|
// If they are valid:
|
|
478
443
|
if (answerData.status === 'ok') {
|
|
479
444
|
// Serve the answer page.
|
|
@@ -485,265 +450,300 @@ const requestHandler = async (request, response) => {
|
|
|
485
450
|
await serveError({message: answerData.error}, response, true);
|
|
486
451
|
}
|
|
487
452
|
}
|
|
453
|
+
// Otherwise, i.e. if the request is invalid:
|
|
454
|
+
else {
|
|
455
|
+
// Report the error.
|
|
456
|
+
await serveError({message: 'ERROR: Invalid retest recommendation'}, response, true);
|
|
457
|
+
}
|
|
488
458
|
}
|
|
489
|
-
// Otherwise,
|
|
490
|
-
else {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
const [url, what] = target.split('\t');
|
|
499
|
-
// If the request is valid:
|
|
500
|
-
if (url.startsWith('https://') && authCode === process.env.AUTH_CODE) {
|
|
501
|
-
// Set the non-location headers for a response.
|
|
502
|
-
setHeaders('text/html', null, 'ultra');
|
|
503
|
-
// If the request is an approval:
|
|
504
|
-
if (what) {
|
|
505
|
-
// Set a location header for a response.
|
|
506
|
-
response.setHeader('content-location', `${pathname}${search}`);
|
|
507
|
-
// Get the answer data.
|
|
508
|
-
const answerData = await require(path.join(__dirname, 'testOrder', 'index'))
|
|
509
|
-
.answer(url, what, authCode);
|
|
510
|
-
// If the answer data are valid:
|
|
511
|
-
if (answerData.status === 'ok') {
|
|
512
|
-
// Serve the answer page.
|
|
513
|
-
response.end(answerData.answerPage);
|
|
459
|
+
// Otherwise, if it is a test recommendation:
|
|
460
|
+
else if (pageName === 'testRec.html') {
|
|
461
|
+
const {what, url, why} = postData;
|
|
462
|
+
// If the request is valid:
|
|
463
|
+
if (what && url.startsWith('https://') && why) {
|
|
464
|
+
// If a report on the page is already available:
|
|
465
|
+
if (await isReportAvailable(what, url)) {
|
|
466
|
+
// Report the error.
|
|
467
|
+
await serveError({message: 'ERROR: Page has already been tested'}, response, true);
|
|
514
468
|
}
|
|
515
|
-
// Otherwise, i.e. if
|
|
469
|
+
// Otherwise, i.e. if no report on the page is available:
|
|
516
470
|
else {
|
|
517
|
-
//
|
|
518
|
-
|
|
471
|
+
// Serve headers for a response.
|
|
472
|
+
setHeaders('text/html', `${pathname}${search}`, 'ultra');
|
|
473
|
+
// Get the answer data.
|
|
474
|
+
const answerData = await require(path.join(__dirname, 'testRec', 'index'))
|
|
475
|
+
.answer(what, url, why);
|
|
476
|
+
// If they are valid:
|
|
477
|
+
if (answerData.status === 'ok') {
|
|
478
|
+
// Serve the answer page.
|
|
479
|
+
response.end(answerData.answerPage);
|
|
480
|
+
}
|
|
481
|
+
// Otherwise, i.e. if they are invalid:
|
|
482
|
+
else {
|
|
483
|
+
// Report the error.
|
|
484
|
+
await serveError({message: answerData.error}, response, true);
|
|
485
|
+
}
|
|
519
486
|
}
|
|
520
487
|
}
|
|
521
|
-
// Otherwise, i.e. if
|
|
488
|
+
// Otherwise, i.e. if the request is invalid:
|
|
522
489
|
else {
|
|
523
|
-
//
|
|
524
|
-
|
|
525
|
-
// Delete the rejected URL.
|
|
526
|
-
delete recs[url];
|
|
527
|
-
// Save the revised recommendations.
|
|
528
|
-
await fs.writeFile(path.join(__dirname, 'jobs', 'recs.json'), getJSON(recs));
|
|
529
|
-
// Set a location header for a response.
|
|
530
|
-
response.setHeader('content-location', '/recActionForm.html');
|
|
531
|
-
// Get the answer data.
|
|
532
|
-
const answerData = await require(path.join(__dirname, 'recActionForm', 'index')).answer();
|
|
533
|
-
// Serve the test-order form.
|
|
534
|
-
response.end(answerData.answerPage);
|
|
490
|
+
// Report the error.
|
|
491
|
+
await serveError({message: 'ERROR: Invalid test recommendation'}, response, true);
|
|
535
492
|
}
|
|
536
493
|
}
|
|
537
|
-
// Otherwise,
|
|
538
|
-
else {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
494
|
+
// Otherwise, if it is an action on a test or retest recommendation:
|
|
495
|
+
else if (pageName === 'recAction.html') {
|
|
496
|
+
const {target, authCode} = postData;
|
|
497
|
+
const [url, what] = target.split('\t');
|
|
498
|
+
// If the request is valid:
|
|
499
|
+
if (url.startsWith('https://') && authCode === process.env.AUTH_CODE) {
|
|
500
|
+
// Set the non-location headers for a response.
|
|
501
|
+
setHeaders('text/html', null, 'ultra');
|
|
502
|
+
// If the request is an approval:
|
|
503
|
+
if (what) {
|
|
504
|
+
// Set a location header for a response.
|
|
505
|
+
response.setHeader('content-location', `${pathname}${search}`);
|
|
506
|
+
// Get the answer data.
|
|
507
|
+
const answerData = await require(path.join(__dirname, 'testOrder', 'index'))
|
|
508
|
+
.answer(url, what, authCode);
|
|
509
|
+
// If the answer data are valid:
|
|
510
|
+
if (answerData.status === 'ok') {
|
|
511
|
+
// Serve the answer page.
|
|
512
|
+
response.end(answerData.answerPage);
|
|
513
|
+
}
|
|
514
|
+
// Otherwise, i.e. if they are invalid:
|
|
515
|
+
else {
|
|
516
|
+
// Report the error.
|
|
517
|
+
await serveError({message: answerData.error}, response, true);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
// Otherwise, i.e. if it is a rejection:
|
|
521
|
+
else {
|
|
522
|
+
// Get the recommendations.
|
|
523
|
+
const recs = await getRecs();
|
|
524
|
+
// Delete the rejected URL.
|
|
525
|
+
delete recs[url];
|
|
526
|
+
// Save the revised recommendations.
|
|
527
|
+
await fs.writeFile(path.join(__dirname, 'jobs', 'recs.json'), getJSON(recs));
|
|
528
|
+
// Set a location header for a response.
|
|
529
|
+
response.setHeader('content-location', '/recActionForm.html');
|
|
530
|
+
// Get the answer data.
|
|
531
|
+
const answerData = await require(path.join(__dirname, 'recActionForm', 'index')).answer();
|
|
532
|
+
// Serve the test-order form.
|
|
533
|
+
response.end(answerData.answerPage);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
// Otherwise, i.e. if the request is invalid:
|
|
537
|
+
else {
|
|
538
|
+
// Report the error.
|
|
539
|
+
await serveError({message: 'ERROR: Invalid test order'}, response, true);
|
|
540
|
+
}
|
|
560
541
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
542
|
+
// Otherwise, if it is a reannotation order:
|
|
543
|
+
else if (pageName === 'reannotate.html') {
|
|
544
|
+
const {authCode} = postData;
|
|
545
|
+
// Set headers for a response.
|
|
546
|
+
setHeaders('text/html', `${pathname}${search}`, 'ultra');
|
|
547
|
+
// Get the answer data.
|
|
548
|
+
const answerData = await require(path.join(__dirname, 'reannotate', 'index'))
|
|
549
|
+
.answer(authCode);
|
|
550
|
+
// If the answer data are valid:
|
|
551
|
+
if (answerData.status === 'ok') {
|
|
552
|
+
// Serve the answer page.
|
|
553
|
+
response.end(answerData.answerPage);
|
|
554
|
+
}
|
|
555
|
+
// Otherwise, i.e. if they are invalid:
|
|
556
|
+
else {
|
|
557
|
+
// Report the error.
|
|
558
|
+
await serveError({message: answerData.error}, response, true);
|
|
559
|
+
}
|
|
574
560
|
}
|
|
575
|
-
// Otherwise,
|
|
576
|
-
else {
|
|
577
|
-
|
|
578
|
-
|
|
561
|
+
// Otherwise, if it is a WCAG map renewal:
|
|
562
|
+
else if (pageName === 'wcagRenew.html') {
|
|
563
|
+
const {authCode} = postData;
|
|
564
|
+
// Set headers for a response.
|
|
565
|
+
setHeaders('text/html', `${pathname}${search}`, 'low');
|
|
566
|
+
// Get the answer data.
|
|
567
|
+
const answerData = await require(path.join(__dirname, 'wcagRenew', 'index'))
|
|
568
|
+
.answer(authCode);
|
|
569
|
+
// If the answer data are valid:
|
|
570
|
+
if (answerData.status === 'ok') {
|
|
571
|
+
// Serve the answer page.
|
|
572
|
+
response.end(answerData.answerPage);
|
|
573
|
+
}
|
|
574
|
+
// Otherwise, i.e. if they are invalid:
|
|
575
|
+
else {
|
|
576
|
+
// Report the error.
|
|
577
|
+
await serveError({message: answerData.error}, response, true);
|
|
578
|
+
}
|
|
579
579
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
580
|
+
// Otherwise, if it is a request from an agent:
|
|
581
|
+
else if (pageName === 'api') {
|
|
582
|
+
// Get the segments of the path after api.
|
|
583
|
+
const segments = pathTail.split('/');
|
|
584
|
+
// If the first segment is the ID of the Testaro agent and the agent is authenticated:
|
|
585
|
+
if (segments[0] === testaroAgent && postData.agentPW === testaroAgentPW) {
|
|
586
|
+
const agentID = segments[0];
|
|
587
|
+
// Get the requested service from the path.
|
|
588
|
+
const service = segments[1];
|
|
589
|
+
// If the service is job assignment:
|
|
590
|
+
if (service === 'job') {
|
|
591
|
+
let clean = true;
|
|
592
|
+
const messageStart = `Testaro agent ${agentID} requested a job, `;
|
|
593
|
+
const jobNames = await getJobNames();
|
|
594
|
+
const claimedJobNames = jobNames.claimed;
|
|
595
|
+
// For each claimed job:
|
|
596
|
+
for (const jobName of claimedJobNames) {
|
|
597
|
+
const job = await getObject(path.join(jobsPath, 'claimed', jobName));
|
|
598
|
+
const {id, sources} = job;
|
|
599
|
+
const {agent} = sources;
|
|
600
|
+
// If its assignee is the agent:
|
|
601
|
+
if (agent === agentID) {
|
|
602
|
+
const messageEnd = `but has not completed job ${id}`;
|
|
603
|
+
// Report this.
|
|
604
|
+
await serveError({message: `${messageStart}${messageEnd}`}, response, false);
|
|
605
|
+
// Reclassify the job as failed.
|
|
606
|
+
await fs.rename(
|
|
607
|
+
path.join(claimedPath, jobName), path.join(failedPath, jobName)
|
|
608
|
+
);
|
|
609
|
+
clean = false;
|
|
610
|
+
// Stop checking claimed jobs.
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
// If no aborted-job error was found for the agent:
|
|
615
|
+
if (clean) {
|
|
616
|
+
const queuedJobNames = jobNames.queue;
|
|
617
|
+
// If any jobs are queued:
|
|
618
|
+
if (queuedJobNames.length) {
|
|
619
|
+
const oldestJobName = queuedJobNames[0];
|
|
620
|
+
// Get the first one.
|
|
621
|
+
const firstJob = await getObject(path.join(queuePath, oldestJobName));
|
|
622
|
+
// Add the agent ID to the job.
|
|
623
|
+
firstJob.sources.agent = agentID;
|
|
624
|
+
console.log(
|
|
625
|
+
`Job ${firstJob.id} (${firstJob.target.what}) is being sent to the agent.`
|
|
626
|
+
);
|
|
627
|
+
// Assign the job to the agent.
|
|
628
|
+
response.writeHead(200, {
|
|
629
|
+
'content-type': 'application/json; charset=utf-8'
|
|
630
|
+
});
|
|
631
|
+
response.end(JSON.stringify(firstJob));
|
|
632
|
+
const messageEnd
|
|
633
|
+
= `and job ${firstJob.id} (${firstJob.target.what}) was assigned to the agent`;
|
|
634
|
+
console.log(`${messageStart}${messageEnd}`);
|
|
635
|
+
// Move the job from the queue to the claimed-jobs directory.
|
|
636
|
+
await fs.rename(
|
|
637
|
+
path.join(queuePath, oldestJobName), path.join(claimedPath, oldestJobName)
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
// Otherwise, i.e. if no jobs are queued:
|
|
641
|
+
else {
|
|
642
|
+
response.writeHead(200, {
|
|
643
|
+
'content-type': 'application/json; charset=utf-8'
|
|
644
|
+
});
|
|
645
|
+
// Send a no-jobs response to the agent.
|
|
646
|
+
response.end(JSON.stringify({}));
|
|
647
|
+
const messageEnd = 'but no job was in the queue';
|
|
648
|
+
console.log(`${messageStart}${messageEnd}`);
|
|
649
|
+
}
|
|
613
650
|
}
|
|
614
651
|
}
|
|
615
|
-
//
|
|
616
|
-
if (
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
//
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
);
|
|
628
|
-
//
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
);
|
|
652
|
+
// Otherwise, if the service is report acquisition:
|
|
653
|
+
else if (service === 'report') {
|
|
654
|
+
const {report} = postData;
|
|
655
|
+
const {id, target} = report;
|
|
656
|
+
const {what, url} = target;
|
|
657
|
+
const [timeStamp, jobID] = id?.split('-') ?? ['', ''];
|
|
658
|
+
// If the request is valid:
|
|
659
|
+
if (id && isTimeStamp(timeStamp) && isJobID(jobID) && what && url) {
|
|
660
|
+
// Acknowledge receipt.
|
|
661
|
+
response.setHeader('content-type', 'application/json; charset=utf-8');
|
|
662
|
+
response.end(JSON.stringify({status: 'ok'}));
|
|
663
|
+
console.log(`Testaro report ${id} was received from Testaro agent ${agentID}`);
|
|
664
|
+
const [timeStamp, jobID] = id.split('-');
|
|
665
|
+
// Save the report.
|
|
666
|
+
await fs.writeFile(getReportPath(timeStamp, jobID), getJSON(report));
|
|
667
|
+
// Create a log for the report.
|
|
668
|
+
const log = {
|
|
669
|
+
what,
|
|
670
|
+
url
|
|
671
|
+
};
|
|
672
|
+
// Save the log.
|
|
673
|
+
await fs.writeFile(getLogPath(timeStamp, jobID), getJSON(log));
|
|
674
|
+
// Annotate the report and mark it as annotated in the log.
|
|
675
|
+
await annotateReport(ruleIDs, timeStamp, jobID);
|
|
676
|
+
console.log(`Testaro report ${id} was annotated, saved, and logged`);
|
|
677
|
+
// Check the monetary balances and send alerts if nearing exhaustion.
|
|
678
|
+
await checkBalancesForAlerts(report);
|
|
679
|
+
// Delete the job.
|
|
680
|
+
await fs.unlink(path.join(claimedPath, `${id}.json`));
|
|
681
|
+
console.log(`Completed job ${id} deleted`);
|
|
640
682
|
}
|
|
641
|
-
// Otherwise, i.e. if
|
|
683
|
+
// Otherwise, i.e. if the request is invalid:
|
|
642
684
|
else {
|
|
643
|
-
|
|
644
|
-
'content-type': 'application/json; charset=utf-8'
|
|
645
|
-
});
|
|
646
|
-
// Send a no-jobs response to the agent.
|
|
647
|
-
response.end(JSON.stringify({}));
|
|
648
|
-
const messageEnd = 'but no job was in the queue';
|
|
649
|
-
console.log(`${messageStart}${messageEnd}`);
|
|
685
|
+
await serveError({message: 'ERROR: Report invalid'}, response, false);
|
|
650
686
|
}
|
|
651
687
|
}
|
|
688
|
+
// Otherwise, if the service is not valid:
|
|
689
|
+
else {
|
|
690
|
+
await serveError(
|
|
691
|
+
{message: 'ERROR: Invalid service request from Testaro agent'}, response, false
|
|
692
|
+
);
|
|
693
|
+
}
|
|
652
694
|
}
|
|
653
|
-
// Otherwise, if the
|
|
654
|
-
else if (
|
|
655
|
-
const {
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
};
|
|
673
|
-
// Save the log.
|
|
674
|
-
await fs.writeFile(getLogPath(timeStamp, jobID), getJSON(log));
|
|
675
|
-
// Annotate the report and mark it as annotated in the log.
|
|
676
|
-
await annotateReport(ruleIDs, timeStamp, jobID);
|
|
677
|
-
console.log(`Testaro report ${id} was annotated, saved, and logged`);
|
|
678
|
-
// Check the monetary balances and send alerts if nearing exhaustion.
|
|
679
|
-
await checkBalancesForAlerts(report);
|
|
680
|
-
// Delete the job.
|
|
681
|
-
await fs.unlink(path.join(claimedPath, `${id}.json`));
|
|
682
|
-
console.log(`Completed job ${id} deleted`);
|
|
695
|
+
// Otherwise, if the first segment is the test recommendation service:
|
|
696
|
+
else if (segments[0] === 'testRecForm') {
|
|
697
|
+
const {what, url, why} = postData;
|
|
698
|
+
// If the payload is a valid test recommendation:
|
|
699
|
+
if (what && isURL(url) && why) {
|
|
700
|
+
// If a report on the page is already available:
|
|
701
|
+
if (await isReportAvailable(what, url)) {
|
|
702
|
+
// Report this.
|
|
703
|
+
await serveError({message: 'ERROR: A report on the page is already available'}, response, false);
|
|
704
|
+
}
|
|
705
|
+
// Otherwise, i.e. if no report on the page is available:
|
|
706
|
+
else {
|
|
707
|
+
// Process the recommendation and get the response data.
|
|
708
|
+
const responseData = await require(path.join(__dirname, 'testRecForm', 'api'))
|
|
709
|
+
.response(what, url, why);
|
|
710
|
+
// Send them.
|
|
711
|
+
setHeaders('application/json', null, 'ultra');
|
|
712
|
+
response.end(JSON.stringify(responseData));
|
|
713
|
+
}
|
|
683
714
|
}
|
|
684
|
-
// Otherwise, i.e. if
|
|
715
|
+
// Otherwise, i.e. if it is not a valid test recommendation:
|
|
685
716
|
else {
|
|
686
|
-
|
|
717
|
+
// Report this.
|
|
718
|
+
await serveError({message: 'ERROR: Invalid test recommendation'}, response, false);
|
|
687
719
|
}
|
|
688
720
|
}
|
|
689
|
-
// Otherwise, if the
|
|
721
|
+
// Otherwise, i.e. if the request is invalid:
|
|
690
722
|
else {
|
|
723
|
+
// Report this.
|
|
691
724
|
await serveError(
|
|
692
|
-
{message: 'ERROR: Invalid
|
|
725
|
+
{message: 'ERROR: Invalid API request'}, response, false
|
|
693
726
|
);
|
|
694
727
|
}
|
|
695
728
|
}
|
|
696
|
-
// Otherwise, if
|
|
697
|
-
else if (
|
|
698
|
-
const {
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
// Report this.
|
|
704
|
-
await serveError({message: 'ERROR: A report on the page is already available'}, response, false);
|
|
705
|
-
}
|
|
706
|
-
// Otherwise, i.e. if no report on the page is available:
|
|
707
|
-
else {
|
|
708
|
-
// Process the recommendation and get the response data.
|
|
709
|
-
const responseData = await require(path.join(__dirname, 'testRecForm', 'api'))
|
|
710
|
-
.response(what, url, why);
|
|
711
|
-
// Send them.
|
|
712
|
-
setHeaders('application/json', null, 'ultra');
|
|
713
|
-
response.end(JSON.stringify(responseData));
|
|
714
|
-
}
|
|
729
|
+
// Otherwise, if it is a tutorial comment:
|
|
730
|
+
else if (pageName === 'tutorialComment.html') {
|
|
731
|
+
const {content} = postData;
|
|
732
|
+
setHeaders('application/json', null, 'low');
|
|
733
|
+
const answerData = await require(path.join(__dirname, 'tutorial', 'index')).saveComment(content);
|
|
734
|
+
if (answerData.status === 'ok') {
|
|
735
|
+
response.end(JSON.stringify({status: 'ok'}));
|
|
715
736
|
}
|
|
716
|
-
// Otherwise, i.e. if it is not a valid test recommendation:
|
|
717
737
|
else {
|
|
718
|
-
|
|
719
|
-
|
|
738
|
+
response.statusCode = 400;
|
|
739
|
+
response.end(JSON.stringify({status: 'error', message: answerData.error}));
|
|
720
740
|
}
|
|
721
741
|
}
|
|
722
|
-
// Otherwise, i.e. if
|
|
742
|
+
// Otherwise, i.e. if it is any other POST request:
|
|
723
743
|
else {
|
|
724
|
-
// Report
|
|
725
|
-
await serveError(
|
|
726
|
-
{message: 'ERROR: Invalid API request'}, response, false
|
|
727
|
-
);
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
// Otherwise, if it is a tutorial comment:
|
|
731
|
-
else if (pageName === 'tutorialComment.html') {
|
|
732
|
-
const {content} = postData;
|
|
733
|
-
setHeaders('application/json', null, 'low');
|
|
734
|
-
const answerData = await require(path.join(__dirname, 'tutorial', 'index')).saveComment(content);
|
|
735
|
-
if (answerData.status === 'ok') {
|
|
736
|
-
response.end(JSON.stringify({status: 'ok'}));
|
|
744
|
+
// Report its invalidity.
|
|
745
|
+
await serveError({message: 'ERROR: Invalid POST request'}, response, true);
|
|
737
746
|
}
|
|
738
|
-
else {
|
|
739
|
-
response.statusCode = 400;
|
|
740
|
-
response.end(JSON.stringify({status: 'error', message: answerData.error}));
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
// Otherwise, i.e. if it is any other POST request:
|
|
744
|
-
else {
|
|
745
|
-
// Report its invalidity.
|
|
746
|
-
await serveError({message: 'ERROR: Invalid POST request'}, response, true);
|
|
747
747
|
}
|
|
748
748
|
}
|
|
749
749
|
// Otherwise, i.e. if it is neither a GET nor a POST request:
|