mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 11:07:08 +00:00
Add ASAQC API mock-up.
To be used for testing and debugging. See index.js for instructions.
This commit is contained in:
156
lib/www/server/queues/asaqc/dummy_server/api/index.js
Normal file
156
lib/www/server/queues/asaqc/dummy_server/api/index.js
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* A minimalist mock-up of the ASAQC API, used
|
||||
* for testing.
|
||||
*
|
||||
* Use the following environment variables to
|
||||
* configure its behaviour:
|
||||
*
|
||||
* HTTP_PORT Port to listen to. Defaults to 3077.
|
||||
*
|
||||
* HTTPS_PEM Combined public and private parts of
|
||||
* a TLS certificate to use. If provided, an HTTPS
|
||||
* server will be started. Alternatively, the user
|
||||
* may provide separate files (see below).
|
||||
*
|
||||
* HTTPS_CERT Public certificate to use in HTTPS
|
||||
* mode.
|
||||
*
|
||||
* HTTPS_KEY Private key to use along `HTTPS_CERT`
|
||||
* in HTTPS mode. Note that both must be provided.
|
||||
*
|
||||
* HTTPS_CA Public certificate of a certificate
|
||||
* authority or the public part of a client certificate.
|
||||
* If provided, the client needs to authenticate with
|
||||
* a certificate signed by this authority, or with this
|
||||
* certifcate itself, if self-signed.
|
||||
*
|
||||
* See also ../index.js for other environment
|
||||
* variable options.
|
||||
*
|
||||
*/
|
||||
const http = require('http');
|
||||
const https = require('https');
|
||||
const fs = require('fs');
|
||||
const express = require('express');
|
||||
|
||||
const mw = require('./middleware');
|
||||
|
||||
const app = express();
|
||||
const verbose = process.env.NODE_ENV != 'test';
|
||||
|
||||
app.map = function(a, route){
|
||||
route = route || '';
|
||||
for (var key in a) {
|
||||
switch (typeof a[key]) {
|
||||
// { '/path': { ... }}
|
||||
case 'object':
|
||||
if (!Array.isArray(a[key])) {
|
||||
app.map(a[key], route + key);
|
||||
break;
|
||||
} // else drop through
|
||||
// get: function(){ ... }
|
||||
case 'function':
|
||||
if (verbose) console.log('%s %s', key, route);
|
||||
app[key](route, a[key]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
app.use(express.json({type: "application/json", strict: false, limit: '10mb'}));
|
||||
app.use(express.urlencoded({ type: "application/x-www-form-urlencoded", extended: true }));
|
||||
app.use(express.text({type: "text/*", limit: '10mb'}));
|
||||
app.use((req, res, next) => {
|
||||
res.set("Access-Control-Allow-Origin", "*");
|
||||
res.set("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PUT, PATCH, DELETE");
|
||||
res.set("Access-Control-Allow-Headers", "Content-Type");
|
||||
next();
|
||||
});
|
||||
|
||||
app.map({
|
||||
'/vt/v1/api/upload-file-encoded': {
|
||||
post: [ mw.post ]
|
||||
}
|
||||
});
|
||||
|
||||
// Generic error handler. Stops stack dumps
|
||||
// being sent to clients.
|
||||
app.use(function (err, req, res, next) {
|
||||
const title = `HTTP backend error at ${req.method} ${req.originalUrl}`;
|
||||
const description = err.message;
|
||||
const message = err.message;
|
||||
const alert = {title, message, description, error: err};
|
||||
|
||||
console.log("Error:", err);
|
||||
|
||||
res.set("Content-Type", "application/json");
|
||||
if (err instanceof Error && err.name != "UnauthorizedError") {
|
||||
console.error(err.stack);
|
||||
res.set("Content-Type", "text/plain");
|
||||
res.status(500).send('General internal error');
|
||||
} else if (typeof err === 'string') {
|
||||
res.status(500).send({message: err});
|
||||
} else {
|
||||
res.status(err.status || 500).send({message: err.message || (err.inner && err.inner.message) || "Internal error"});
|
||||
}
|
||||
});
|
||||
|
||||
app.disable('x-powered-by');
|
||||
app.enable('trust proxy');
|
||||
console.log('trust proxy is ' + (app.get('trust proxy')? 'on' : 'off'));
|
||||
|
||||
const addr = "127.0.0.1";
|
||||
|
||||
if (!module.parent) {
|
||||
var port = process.env.HTTP_PORT || 3000;
|
||||
var server = http.createServer(app).listen(port, addr);
|
||||
|
||||
console.log('API started on port ' + port);
|
||||
} else {
|
||||
app.start = function (port = 3000, path, locals={}) {
|
||||
|
||||
app.locals = {...app.locals, ...locals};
|
||||
|
||||
var root = app;
|
||||
if (path) {
|
||||
root = express();
|
||||
['x-powered-by', 'trust proxy'].forEach(k => root.set(k, app.get(k)));
|
||||
root.use(path, app);
|
||||
}
|
||||
|
||||
if (process.env.HTTPS_PEM || (process.env.HTTPS_CERT && process.env.HTTPS_KEY)) {
|
||||
|
||||
const options = {
|
||||
cert: fs.readFileSync(process.env.HTTPS_CERT || process.env.HTTPS_PEM),
|
||||
key: fs.readFileSync(process.env.HTTPS_KEY || process.env.HTTPS_PEM)
|
||||
};
|
||||
|
||||
if (process.env.HTTPS_CA) {
|
||||
// Enable client certificate authentication
|
||||
options.requestCert = true;
|
||||
options.ca = fs.readFileSync(process.env.HTTPS_CA);
|
||||
options.rejectUnauthorized = true;
|
||||
console.log("TLS client authentication requested");
|
||||
}
|
||||
|
||||
const server = https.createServer(options, root).listen(port, addr);
|
||||
|
||||
if (server) {
|
||||
console.log(`TLS API started on port ${port}, prefix: ${path || "/"}`);
|
||||
}
|
||||
|
||||
return server;
|
||||
} else {
|
||||
|
||||
const server = http.createServer(root).listen(port, addr);
|
||||
|
||||
if (server) {
|
||||
console.log(`API started on port ${port}, prefix: ${path || "/"}`);
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
}
|
||||
module.exports = app;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
post: require('./post')
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const uuid = require('uuid/v4');
|
||||
|
||||
/**
|
||||
* Suggest new names for files.
|
||||
*
|
||||
* Takes a file name and adds a numeric suffix to it if
|
||||
* there isn't one, or increments it otherwise.
|
||||
*/
|
||||
function rename(filename) {
|
||||
const ext = path.extname(filename);
|
||||
const base = path.basename(filename, ext);
|
||||
const bare = base.match(/^(.*?)(-\d+)?$/)[1]; // Strips out any -\d+ suffix
|
||||
const suffix = Number((base.match(/-(\d+)$/) || [])[1]) || 0; // Because NaN
|
||||
return `${bare}-${suffix+1}${ext}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save `data` as `filename` in `dirname`.
|
||||
*/
|
||||
async function saveFile (dirname, filename, data) {
|
||||
const fname = path.resolve(dirname, filename);
|
||||
if (fs.existsSync(fname)) {
|
||||
return await saveFile(dirname, rename(filename), data);
|
||||
} else {
|
||||
await fs.promises.writeFile(fname, data);
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method imitates the ASAQC endpoint
|
||||
* /vt/v1/api/upload-file-encoded
|
||||
*
|
||||
* If OUTPUT_DIR is defined, it tries to save the
|
||||
* received data as files in that directory, else
|
||||
* it discards the data but still produces a
|
||||
* plausible response.
|
||||
*/
|
||||
module.exports = async function (req, res, next) {
|
||||
|
||||
try {
|
||||
const payload = req.body;
|
||||
const response = {id: uuid()};
|
||||
|
||||
if (payload.fileName && payload.encodedData) {
|
||||
const data = Buffer.from(payload.encodedData, 'base64');
|
||||
|
||||
console.log(`Received ${payload.fileName}, ${data.length} bytes`);
|
||||
|
||||
if (req.app.locals.OUTPUT_DIR) {
|
||||
response.fileName = await saveFile(req.app.locals.OUTPUT_DIR, payload.fileName, data);
|
||||
} else {
|
||||
console.log("No disk output");
|
||||
}
|
||||
|
||||
res.send(response);
|
||||
} else {
|
||||
res.status(400).send({message: "Bad syntax"});
|
||||
}
|
||||
next();
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
|
||||
};
|
||||
29
lib/www/server/queues/asaqc/dummy_server/index.js
Executable file
29
lib/www/server/queues/asaqc/dummy_server/index.js
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/node
|
||||
|
||||
/**
|
||||
* A minimalist mock-up of the ASAQC server, used
|
||||
* for testing.
|
||||
*
|
||||
* Use the following environment variables to
|
||||
* configure its behaviour:
|
||||
*
|
||||
* ASAQC_DUMMY_OUTPUT_DIR Path to a directory in which
|
||||
* to store the received data. If not provided, the
|
||||
* data will be discarded but the API will still return
|
||||
* a success code. Alternatively, a path may be given
|
||||
* in the command line.
|
||||
*
|
||||
* See also api/index.js for other environment
|
||||
* variable options.
|
||||
*
|
||||
* Example command line:
|
||||
*
|
||||
* HTTPS_CA="./certs/client.pem" HTTPS_PEM="./certs/dougal.pem" ./index.js ./store
|
||||
*
|
||||
*/
|
||||
|
||||
const api = require('./api');
|
||||
|
||||
const OUTPUT_DIR = process.argv[2] || process.env.ASAQC_DUMMY_OUTPUT_DIR;
|
||||
|
||||
const server = api.start(process.env.HTTP_PORT || 3077, process.env.HTTP_PATH, {OUTPUT_DIR});
|
||||
Reference in New Issue
Block a user