mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2025-04-27 14:56:21 -04:00
* convert GitHub cards grid from lit-html to preact * give hit counter the preact treatment * extract loading spinner component to a shared location * move *some* loading spinner styles to its JSX * Update .percy.yml * pick up images in JS w/ webpack * pull star/fork icons straight from @primer/octicons * a bit of cleanup * check `typeof window !== "undefined"` before rendering * bump misc. deps * silence missing license warnings for preact-hooks and preact-compat * add source-map-loader * Update loading.js
101 lines
3.4 KiB
JavaScript
101 lines
3.4 KiB
JavaScript
import * as Sentry from "@sentry/node";
|
|
import fetch from "node-fetch";
|
|
import getStream from "get-stream";
|
|
|
|
Sentry.init({
|
|
dsn: process.env.SENTRY_DSN || "",
|
|
environment: process.env.NODE_ENV || process.env.VERCEL_ENV || "",
|
|
});
|
|
|
|
// this "proxy" to report-uri.com is temporary until I'm bored enough to make my own reporting API from scratch
|
|
// https://report-uri.com/account/setup/
|
|
const REPORT_URI_SUBDOMAIN = "jarvis";
|
|
|
|
export default async (req, res) => {
|
|
try {
|
|
// permissive access control headers
|
|
res.setHeader("Access-Control-Allow-Methods", "POST");
|
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
// disable caching on both ends
|
|
res.setHeader("Cache-Control", "private, no-cache, no-store, must-revalidate");
|
|
res.setHeader("Expires", 0);
|
|
res.setHeader("Pragma", "no-cache");
|
|
|
|
if (req.method !== "POST") {
|
|
return res.status(405).send(); // 405 Method Not Allowed
|
|
}
|
|
|
|
// start parsing body manually, since the serverless helper functions don't recognize `application/csp-report` and
|
|
// `application/reports` as JSON to be parsed:
|
|
// https://vercel.com/docs/runtimes#official-runtimes/node-js/node-js-request-and-response-objects/request-body
|
|
const body = JSON.parse(await getStream(req));
|
|
|
|
// default to returning 400 Bad Request for an invalid POST request
|
|
let statusCode = 400;
|
|
|
|
// TODO: add Expect-CT reporting endpoint
|
|
if (typeof req.query.csp !== "undefined" && req.headers["content-type"].startsWith("application/csp-report")) {
|
|
// send a CSP violation:
|
|
// https://docs.report-uri.com/setup/csp/
|
|
statusCode = await sendCsp(body, req.headers);
|
|
} else if (req.headers["content-type"].startsWith("application/reports")) {
|
|
// send a report:
|
|
// https://docs.report-uri.com/setup/reporting-api/
|
|
statusCode = await sendReport(body, req.headers);
|
|
}
|
|
|
|
return res.status(statusCode).send();
|
|
} catch (error) {
|
|
console.error(error);
|
|
|
|
// log error to sentry, give it 2 seconds to finish sending
|
|
Sentry.captureException(error);
|
|
await Sentry.flush(2000);
|
|
|
|
return res.status(500).send(); // 500 Internal Server Error
|
|
}
|
|
};
|
|
|
|
const sendCsp = async (body, headers) => {
|
|
// filter out any last invalid reports (JSON must have at least one csp-report object)
|
|
if (Object.hasOwnProperty.call(body, "csp-report")) {
|
|
const response = await fetch(`https://${REPORT_URI_SUBDOMAIN}.report-uri.com/r/d/csp/enforce`, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/csp-report",
|
|
"User-Agent": headers["user-agent"],
|
|
Origin: headers["origin"],
|
|
},
|
|
body: JSON.stringify(body),
|
|
});
|
|
|
|
// API returns 201 Created if successful
|
|
if (response.status !== 201) {
|
|
console.error(`[CSP] ${response.status}: ${await response.text()}`);
|
|
}
|
|
|
|
return response.status;
|
|
}
|
|
|
|
return 400; // 400 Bad Request
|
|
};
|
|
|
|
const sendReport = async (body, headers) => {
|
|
const response = await fetch(`https://${REPORT_URI_SUBDOMAIN}.report-uri.com/a/d/g`, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/reports+json",
|
|
"User-Agent": headers["user-agent"],
|
|
Origin: headers["origin"],
|
|
},
|
|
body: JSON.stringify(body),
|
|
});
|
|
|
|
// API returns 201 Created if successful
|
|
if (response.status !== 201) {
|
|
console.error(`[REPORT] ${response.status}: ${await response.text()}`);
|
|
}
|
|
|
|
return response.status;
|
|
};
|