microfeedback-core
Version:
The core library for creating microfeedback backends.
111 lines (106 loc) • 3.82 kB
JavaScript
const {send, json, createError} = require('micro');
const akismetAPI = require('akismet-api');
const Perspective = require('perspective-api-client');
const microCors = require('micro-cors');
const pkg = require('./package.json');
const cors = microCors({allowMethods: ['GET', 'POST', 'OPTIONS']});
const perspectiveEnabled = Boolean(process.env.PERSPECTIVE_API_KEY && process.env.PERSPECTIVE_ENABLED !== 'false');
const akismetEnabled = Boolean(process.env.AKISMET_API_KEY && process.env.AKISMET_ENABLED !== 'false');
/**
* Catch errors from the wrapped function.
* If any errors are caught, a JSON response is generated for that error.
*/
const handleErrors = fn => async (req, res) => {
try {
return await fn(req, res);
} catch (err) {
if (process.env.NODE_ENV === 'dev' && err.stack) {
console.error(err.stack);
}
const status = err.statusCode || 500;
send(res, status, {
status,
message: err.message,
});
}
};
/**
* Factory for a micro handler containing the main routing logic.
*
* @param Function backend: The backend to use for the service. The backend receives the
* parsed input JSON from the client.
* @param Object attributes: Optional attributes about the backend, e.g. name, version.
*/
module.exports = (backend, attributes) =>
handleErrors(
cors(async (req, res) => {
if (req.method === 'GET') {
const response = {
message:
'Welcome to the microfeedback API. Send a POST ' +
'request to send feedback.',
core: {
version: pkg.version,
perspectiveEnabled,
akismetEnabled,
},
};
if (attributes) {
response.backend = attributes;
}
send(res, 200, response);
} else if (req.method === 'POST') {
const input = await json(req);
if (!input.body) {
throw createError(422, '"body" is required in request payload');
}
let perspective = null;
let akismet = null;
if (perspectiveEnabled) {
const perspectiveClient = new Perspective(({apiKey: process.env.PERSPECTIVE_API_KEY}));
try {
const response = await perspectiveClient.analyze(input.body, {truncate: true});
const toxicity = response.attributeScores.TOXICITY.summaryScore.value;
perspective = {toxicity};
} catch (err) {
console.error(err.stack || err);
}
}
if (akismetEnabled) {
const akismetClient = akismetAPI.client({
key: process.env.AKISMET_API_KEY,
blog: req.headers.origin,
});
let spam = null;
try {
/* eslint-disable camelcase */
spam = await akismetClient.checkSpam({
user_ip: req.headers['remote-addr'],
user_agent: req.headers['user-agent'],
referrer: req.headers.referer,
comment_type: 'comment',
comment_content: input.body || '',
});
/* eslint-enable camelcase */
} catch (err) {
console.error(err.stack || err);
}
if (spam !== null) {
const allowSpam = Boolean(process.env.ALLOW_SPAM && process.env.ALLOW_SPAM !== 'false');
if (spam && !allowSpam) {
throw createError(400, 'Spam detected.');
}
akismet = {spam};
}
}
const result = await backend({input, perspective, akismet}, req, res);
const responseData = {result};
if (attributes) {
responseData.backend = attributes;
}
send(res, 201, responseData);
} else {
throw createError(405, `Method ${req.method} not allowed.`);
}
})
);