Skip to content

Commit ae94fdf

Browse files
committed
✨ Started working on server-side endoint for action buttons (gchq#383)
1 parent 32ca899 commit ae94fdf

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed

server.js

+11
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ require('./services/config-validator'); // Include and kicks off the config file
2222

2323
/* Include route handlers for API endpoints */
2424
const statusCheck = require('./services/status-check'); // Used by the status check feature, uses GET
25+
const actionButton = require('./services/action-buttons-call'); // Used by action buttons, to execute users web hooks
2526
const saveConfig = require('./services/save-config'); // Saves users new conf.yml to file-system
2627
const rebuild = require('./services/rebuild-app'); // A script to programmatically trigger a build
2728
const systemInfo = require('./services/system-info'); // Basic system info, for resource widget
@@ -84,6 +85,16 @@ const app = express()
8485
printWarning(`Error running status check for ${req.url}\n`, e);
8586
}
8687
})
88+
// GET endpoint for executing web-hooks for action buttons
89+
.use(ENDPOINTS.actionButtonCall, (req, res) => {
90+
try {
91+
actionButton(req.url, async (results) => {
92+
await res.end(results);
93+
});
94+
} catch (e) {
95+
printWarning(`Error executing web hook for ${req.url}\n`, e);
96+
}
97+
})
8798
// POST Endpoint used to save config, by writing conf.yml to disk
8899
.use(ENDPOINTS.save, method('POST', (req, res) => {
89100
try {

services/action-buttons-call.js

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/**
2+
* Endpoint called from the client, to execute action buttons,
3+
* by making GET request to web hook URL, and returning response
4+
*/
5+
const axios = require('axios').default;
6+
const https = require('https');
7+
8+
/* Makes human-readable response text for failed check */
9+
const makeErrorMessage = (data) => `❌ Service Unavailable: ${data.hostname || 'Server'} `
10+
+ `resulted in ${data.code || 'a fatal error'} ${data.errno ? `(${data.errno})` : ''}`;
11+
12+
/* Kicks of a HTTP request, then formats and renders results */
13+
const makeRequest = (url, options, render) => {
14+
const {
15+
headers, enableInsecure, maxRedirects,
16+
} = options;
17+
const startTime = new Date();
18+
// Create HTTPS agent for request
19+
const requestMaker = axios.create({
20+
httpsAgent: new https.Agent({
21+
rejectUnauthorized: !enableInsecure,
22+
}),
23+
});
24+
// Make request, with params
25+
requestMaker.request({
26+
url,
27+
headers,
28+
maxRedirects,
29+
}).then((response) => ({
30+
statusCode: response.status,
31+
responseText: response.statusText,
32+
successStatus: true,
33+
timeTaken: (new Date() - startTime),
34+
})).catch((error) => ({
35+
successStatus: false,
36+
message: makeErrorMessage(error),
37+
})).then((results) => {
38+
// Request completed (either successfully, or failed) - render results
39+
render(JSON.stringify(results));
40+
});
41+
};
42+
43+
const decodeHeaders = (maybeHeaders) => {
44+
if (!maybeHeaders) return {};
45+
const decodedHeaders = decodeURIComponent(maybeHeaders);
46+
let parsedHeaders = {};
47+
try {
48+
parsedHeaders = JSON.parse(decodedHeaders);
49+
} catch (e) { /* Not valid JSON, will just return false */ }
50+
return parsedHeaders;
51+
};
52+
53+
/* Returned if the URL param is not present or correct */
54+
const immediateError = (render) => {
55+
render(JSON.stringify({
56+
successStatus: false,
57+
message: '❌ Missing URL or Malformed Options',
58+
}));
59+
};
60+
61+
/* Main function, will check if a URL present, and call function */
62+
module.exports = (paramStr, render) => {
63+
// If no parameters passed, then fail
64+
if (!paramStr || !paramStr.includes('=')) {
65+
immediateError(render);
66+
return;
67+
}
68+
// Prepare the parameters, which are got from the URL
69+
const params = new URLSearchParams(paramStr);
70+
const url = decodeURIComponent(params.get('url'));
71+
const maxRedirects = decodeURIComponent(params.get('maxRedirects')) || 0;
72+
const headers = decodeHeaders(params.get('headers'));
73+
const enableInsecure = !!params.get('enableInsecure');
74+
// Check target URL is present
75+
if (!url || url === 'undefined') immediateError(render);
76+
// Put options together
77+
const options = {
78+
headers, enableInsecure, maxRedirects,
79+
};
80+
// Make the request
81+
makeRequest(url, options, render);
82+
};

0 commit comments

Comments
 (0)