Refactor API server to be type safe

This commit is contained in:
Jordan Eldredge 2020-11-18 16:34:37 -05:00
parent 6c15aa5c53
commit 6c96817117
7 changed files with 110 additions and 81 deletions

View file

@ -0,0 +1,68 @@
import router from "./router";
import fileUpload from "express-fileupload";
import cors, { CorsOptions } from "cors";
import bodyParser from "body-parser";
import Sentry from "@sentry/node";
import expressSitemapXml from "express-sitemap-xml";
import * as Skins from "../data/skins";
import express from "express";
export function createApp() {
const app = express();
app.use(Sentry.Handlers.requestHandler());
// Configure CORs
app.use(cors(corsOptions));
app.options("*", cors(corsOptions));
// Configure json output
app.set("json spaces", 2);
// parse application/json
app.use(bodyParser.json());
// Configure File Uploads
const limits = { fileSize: 50 * 1024 * 1024 };
app.use(fileUpload({ limits }));
// Configure sitemap
app.use(expressSitemapXml(getSitemapUrls, "https://skins.webamp.org"));
// Add routes
app.use("/", router);
// The error handler must be before any other error middleware and after all controllers
app.use(Sentry.Handlers.errorHandler());
// Optional fallthrough error handler
app.use(function onError(err, req, res, next) {
res.statusCode = 500;
res.json({ errorId: res.sentry, message: err.message });
});
return app;
}
async function getSitemapUrls() {
const md5s = await Skins.getAllClassicSkins();
const skinUrls = md5s.map(({ md5, fileName }) => `skin/${md5}/${fileName}`);
return ["/about", "/", "/upload", ...skinUrls];
}
const allowList = [
/https:\/\/skins\.webamp\.org/,
/http:\/\/localhost:3000/,
/netlify.app/,
];
const corsOptions: CorsOptions = {
origin: function (origin, callback) {
if (!origin || allowList.some((regex) => regex.test(origin))) {
callback(null, true);
} else {
callback(
new Error(`Request from origin "${origin}" not allowed by CORS.`)
);
}
},
};

View file

@ -0,0 +1,18 @@
import Sentry from "@sentry/node";
import { createApp } from "./app";
const port = process.env.PORT ? Number(process.env.PORT) : 3001;
// GO!
const app = createApp();
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
// Initialize Sentry after we start listening. Any crash at start time will appear in the console and we'll notice.
Sentry.init({
dsn:
"https://0e6bc841b4f744b2953a1fe5981effe6@o68382.ingest.sentry.io/5508241",
// We recommend adjusting this value in production, or using tracesSampler
// for finer control
tracesSampleRate: 1.0,
});

View file

@ -3,7 +3,6 @@ import path from "path";
import md5Hash from "md5";
import { searchIndex } from "../algolia";
import { truncate, MD5_REGEX } from "../utils";
import { TweetStatus } from "../types";
import fetch from "node-fetch";
import * as S3 from "../s3";
import SkinModel from "./SkinModel";
@ -557,7 +556,7 @@ LIMIT ? offset ?`,
}
export async function getAllClassicSkins(): Promise<
Array<{ fileName: string; url: string }>
Array<{ fileName: string; md5: string }>
> {
const skins = await knex.raw(
`

View file

@ -1,77 +0,0 @@
const express = require("express");
const app = express();
const Skins = require("./data/skins");
const port = process.env.PORT ? Number(process.env.PORT) : 3001;
const fileUpload = require("express-fileupload");
const cors = require("cors");
var bodyParser = require("body-parser");
const expressSitemapXml = require("express-sitemap-xml");
const router = require("./router");
const Sentry = require("@sentry/node");
// or use es6 import statements
// import * as Sentry from '@sentry/node';
Sentry.init({
dsn:
"https://0e6bc841b4f744b2953a1fe5981effe6@o68382.ingest.sentry.io/5508241",
// We recommend adjusting this value in production, or using tracesSampler
// for finer control
tracesSampleRate: 1.0,
});
const allowList = [
/https:\/\/skins\.webamp\.org/,
/http:\/\/localhost:3000/,
/netlify.app/,
];
const corsOptions = {
origin: function (origin, callback) {
if (allowList.some((regex) => regex.test(origin)) || !origin) {
callback(null, true);
} else {
callback(
new Error(`Request from origin "${origin}" not allowed by CORS.`)
);
}
},
};
async function getUrls() {
const md5s = await Skins.getAllClassicSkins();
const skinUrls = md5s.map(({ md5, fileName }) => `skin/${md5}/${fileName}`);
return ["/about", "/", "/upload", ...skinUrls];
}
app.use(expressSitemapXml(getUrls, "https://skins.webamp.org"));
// parse application/json
app.use(bodyParser.json());
app.use(Sentry.Handlers.requestHandler());
app.use(cors(corsOptions));
app.options("*", cors(corsOptions));
// TODO: Look into 766c4fad9088037ab4839b18292be8b1
// Has huge number of filenames in info.json
app.set("json spaces", 2);
app.use(
fileUpload({
limits: { fileSize: 50 * 1024 * 1024 },
})
);
app.use(router);
// The error handler must be before any other error middleware and after all controllers
app.use(Sentry.Handlers.errorHandler());
// Optional fallthrough error handler
app.use(function onError(err, req, res, next) {
res.statusCode = 500;
res.json({ errorId: res.sentry, message: err.message });
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));

View file

@ -6,7 +6,10 @@
"dependencies": {
"@sentry/node": "^5.27.3",
"@sentry/tracing": "^5.27.3",
"@types/cors": "^2.8.8",
"@types/express": "^4.17.9",
"@types/express-fileupload": "^1.1.5",
"@types/express-sitemap-xml": "^1.1.1",
"@types/lru-cache": "^5.1.0",
"@types/node-fetch": "^2.5.7",
"algoliasearch": "^4.3.0",
@ -37,7 +40,7 @@
"yargs": "^13.2.4"
},
"scripts": {
"start": "ts-node --transpile-only index.js",
"start": "ts-node --transpile-only api/server.js",
"tweet": "ts-node --transpile-only ./cli.ts tweet",
"fetch-metadata": "ts-node --transpile-only ./cli.ts fetch-metadata",
"bot": "ts-node --transpile-only ./discord-bot/index.js",

View file

@ -1687,6 +1687,12 @@
dependencies:
"@types/node" "*"
"@types/cors@^2.8.8":
version "2.8.8"
resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.8.tgz#317a8d8561995c60e35b9e0fcaa8d36660c98092"
dependencies:
"@types/express" "*"
"@types/css-font-loading-module@^0.0.2":
version "0.0.2"
resolved "https://registry.yarnpkg.com/@types/css-font-loading-module/-/css-font-loading-module-0.0.2.tgz#09f1f1772975777e37851b7b7a4389d97c210add"
@ -1699,6 +1705,12 @@
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
"@types/express-fileupload@^1.1.5":
version "1.1.5"
resolved "https://registry.yarnpkg.com/@types/express-fileupload/-/express-fileupload-1.1.5.tgz#dc8a971f9060d50b12e3908a09248f1772e99484"
dependencies:
"@types/express" "*"
"@types/express-serve-static-core@*":
version "4.17.13"
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.13.tgz#d9af025e925fc8b089be37423b8d1eac781be084"
@ -1707,7 +1719,13 @@
"@types/qs" "*"
"@types/range-parser" "*"
"@types/express@^4.17.9":
"@types/express-sitemap-xml@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/express-sitemap-xml/-/express-sitemap-xml-1.1.1.tgz#be4c20cd458e8d22cd742db34623100579cc1c49"
dependencies:
"@types/express" "*"
"@types/express@*", "@types/express@^4.17.9":
version "4.17.9"
resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.9.tgz#f5f2df6add703ff28428add52bdec8a1091b0a78"
dependencies: