From 99ee7e9400f06e8718612d8b52d15215dc620774 Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Mon, 26 Sep 2022 22:29:30 +1000 Subject: Prettier --- src/api/middlewares/Authentication.ts | 18 ++++++--- src/api/middlewares/BodyParser.ts | 3 +- src/api/middlewares/CORS.ts | 12 ++++-- src/api/middlewares/ErrorHandler.ts | 25 ++++++++++--- src/api/middlewares/RateLimit.ts | 70 ++++++++++++++++++++++++++--------- src/api/middlewares/Translation.ts | 14 +++++-- 6 files changed, 107 insertions(+), 35 deletions(-) (limited to 'src/api/middlewares') diff --git a/src/api/middlewares/Authentication.ts b/src/api/middlewares/Authentication.ts index 1df7911b..50048b12 100644 --- a/src/api/middlewares/Authentication.ts +++ b/src/api/middlewares/Authentication.ts @@ -10,7 +10,7 @@ export const NO_AUTHORIZATION_ROUTES = [ "/auth/mfa/totp", // Routes with a seperate auth system "/webhooks/", - // Public information endpoints + // Public information endpoints "/ping", "/gateway", "/experiments", @@ -26,7 +26,7 @@ export const NO_AUTHORIZATION_ROUTES = [ // Public policy pages "/policies/instance", // Asset delivery - /\/guilds\/\d+\/widget\.(json|png)/ + /\/guilds\/\d+\/widget\.(json|png)/, ]; export const API_PREFIX = /^\/api(\/v\d+)?/; @@ -43,7 +43,11 @@ declare global { } } -export async function Authentication(req: Request, res: Response, next: NextFunction) { +export async function Authentication( + req: Request, + res: Response, + next: NextFunction, +) { if (req.method === "OPTIONS") return res.sendStatus(204); const url = req.url.replace(API_PREFIX, ""); if (url.startsWith("/invites") && req.method === "GET") return next(); @@ -54,12 +58,16 @@ export async function Authentication(req: Request, res: Response, next: NextFunc }) ) return next(); - if (!req.headers.authorization) return next(new HTTPError("Missing Authorization Header", 401)); + if (!req.headers.authorization) + return next(new HTTPError("Missing Authorization Header", 401)); try { const { jwtSecret } = Config.get().security; - const { decoded, user }: any = await checkToken(req.headers.authorization, jwtSecret); + const { decoded, user }: any = await checkToken( + req.headers.authorization, + jwtSecret, + ); req.token = decoded; req.user_id = decoded.id; diff --git a/src/api/middlewares/BodyParser.ts b/src/api/middlewares/BodyParser.ts index 4cb376bc..7741f1fd 100644 --- a/src/api/middlewares/BodyParser.ts +++ b/src/api/middlewares/BodyParser.ts @@ -6,7 +6,8 @@ export function BodyParser(opts?: OptionsJson) { const jsonParser = bodyParser.json(opts); return (req: Request, res: Response, next: NextFunction) => { - if (!req.headers["content-type"]) req.headers["content-type"] = "application/json"; + if (!req.headers["content-type"]) + req.headers["content-type"] = "application/json"; jsonParser(req, res, (err) => { if (err) { diff --git a/src/api/middlewares/CORS.ts b/src/api/middlewares/CORS.ts index 20260cf9..2dce51c6 100644 --- a/src/api/middlewares/CORS.ts +++ b/src/api/middlewares/CORS.ts @@ -7,10 +7,16 @@ export function CORS(req: Request, res: Response, next: NextFunction) { // TODO: use better CSP res.set( "Content-security-policy", - "default-src * data: blob: filesystem: about: ws: wss: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src * data: blob: ; style-src * data: blob: 'unsafe-inline'; font-src * data: blob: 'unsafe-inline';" + "default-src * data: blob: filesystem: about: ws: wss: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src * data: blob: ; style-src * data: blob: 'unsafe-inline'; font-src * data: blob: 'unsafe-inline';", + ); + res.set( + "Access-Control-Allow-Headers", + req.header("Access-Control-Request-Headers") || "*", + ); + res.set( + "Access-Control-Allow-Methods", + req.header("Access-Control-Request-Methods") || "*", ); - res.set("Access-Control-Allow-Headers", req.header("Access-Control-Request-Headers") || "*"); - res.set("Access-Control-Allow-Methods", req.header("Access-Control-Request-Methods") || "*"); next(); } diff --git a/src/api/middlewares/ErrorHandler.ts b/src/api/middlewares/ErrorHandler.ts index 2012b91c..bf3b011a 100644 --- a/src/api/middlewares/ErrorHandler.ts +++ b/src/api/middlewares/ErrorHandler.ts @@ -3,7 +3,12 @@ import { HTTPError } from "lambert-server"; import { ApiError, FieldError } from "@fosscord/util"; const EntityNotFoundErrorRegex = /"(\w+)"/; -export function ErrorHandler(error: Error, req: Request, res: Response, next: NextFunction) { +export function ErrorHandler( + error: Error, + req: Request, + res: Response, + next: NextFunction, +) { if (!error) return next(); try { @@ -12,20 +17,28 @@ export function ErrorHandler(error: Error, req: Request, res: Response, next: Ne let message = error?.toString(); let errors = undefined; - if (error instanceof HTTPError && error.code) code = httpcode = error.code; + if (error instanceof HTTPError && error.code) + code = httpcode = error.code; else if (error instanceof ApiError) { code = error.code; message = error.message; httpcode = error.httpStatus; } else if (error.name === "EntityNotFoundError") { - message = `${error.message.match(EntityNotFoundErrorRegex)?.[1] || "Item"} could not be found`; + message = `${ + error.message.match(EntityNotFoundErrorRegex)?.[1] || "Item" + } could not be found`; code = httpcode = 404; } else if (error instanceof FieldError) { code = Number(error.code); message = error.message; errors = error.errors; } else { - console.error(`[Error] ${code} ${req.url}\n`, errors || error, "\nbody:", req.body); + console.error( + `[Error] ${code} ${req.url}\n`, + errors || error, + "\nbody:", + req.body, + ); if (req.server?.options?.production) { // don't expose internal errors to the user, instead human errors should be thrown as HTTPError @@ -39,6 +52,8 @@ export function ErrorHandler(error: Error, req: Request, res: Response, next: Ne res.status(httpcode).json({ code: code, message, errors }); } catch (error) { console.error(`[Internal Server Error] 500`, error); - return res.status(500).json({ code: 500, message: "Internal Server Error" }); + return res + .status(500) + .json({ code: 500, message: "Internal Server Error" }); } } diff --git a/src/api/middlewares/RateLimit.ts b/src/api/middlewares/RateLimit.ts index 57645c0b..b3976a16 100644 --- a/src/api/middlewares/RateLimit.ts +++ b/src/api/middlewares/RateLimit.ts @@ -40,21 +40,32 @@ export default function rateLimit(opts: { success?: boolean; onlyIp?: boolean; }): any { - return async (req: Request, res: Response, next: NextFunction): Promise => { + return async ( + req: Request, + res: Response, + next: NextFunction, + ): Promise => { // exempt user? if so, immediately short circuit if (req.user_id) { const rights = await getRights(req.user_id); if (rights.has("BYPASS_RATE_LIMITS")) return next(); } - const bucket_id = opts.bucket || req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, ""); + const bucket_id = + opts.bucket || + req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, ""); let executor_id = getIpAdress(req); if (!opts.onlyIp && req.user_id) executor_id = req.user_id; let max_hits = opts.count; if (opts.bot && req.user_bot) max_hits = opts.bot; - if (opts.GET && ["GET", "OPTIONS", "HEAD"].includes(req.method)) max_hits = opts.GET; - else if (opts.MODIFY && ["POST", "DELETE", "PATCH", "PUT"].includes(req.method)) max_hits = opts.MODIFY; + if (opts.GET && ["GET", "OPTIONS", "HEAD"].includes(req.method)) + max_hits = opts.GET; + else if ( + opts.MODIFY && + ["POST", "DELETE", "PATCH", "PUT"].includes(req.method) + ) + max_hits = opts.MODIFY; let offender = Cache.get(executor_id + bucket_id); @@ -75,11 +86,15 @@ export default function rateLimit(opts: { const global = bucket_id === "global"; // each block violation pushes the expiry one full window further reset += opts.window * 1000; - offender.expires_at = new Date(offender.expires_at.getTime() + opts.window * 1000); + offender.expires_at = new Date( + offender.expires_at.getTime() + opts.window * 1000, + ); resetAfterMs = reset - Date.now(); resetAfterSec = Math.ceil(resetAfterMs / 1000); - console.log(`blocked bucket: ${bucket_id} ${executor_id}`, { resetAfterMs }); + console.log(`blocked bucket: ${bucket_id} ${executor_id}`, { + resetAfterMs, + }); return ( res .status(429) @@ -91,20 +106,33 @@ export default function rateLimit(opts: { .set("Retry-After", `${Math.ceil(resetAfterSec)}`) .set("X-RateLimit-Bucket", `${bucket_id}`) // TODO: error rate limit message translation - .send({ message: "You are being rate limited.", retry_after: resetAfterSec, global }) + .send({ + message: "You are being rate limited.", + retry_after: resetAfterSec, + global, + }) ); } } next(); - const hitRouteOpts = { bucket_id, executor_id, max_hits, window: opts.window }; + const hitRouteOpts = { + bucket_id, + executor_id, + max_hits, + window: opts.window, + }; if (opts.error || opts.success) { res.once("finish", () => { // check if error and increment error rate limit if (res.statusCode >= 400 && opts.error) { return hitRoute(hitRouteOpts); - } else if (res.statusCode >= 200 && res.statusCode < 300 && opts.success) { + } else if ( + res.statusCode >= 200 && + res.statusCode < 300 && + opts.success + ) { return hitRoute(hitRouteOpts); } }); @@ -141,8 +169,8 @@ export async function initRateLimits(app: Router) { rateLimit({ bucket: "global", onlyIp: true, - ...ip - }) + ...ip, + }), ); app.use(rateLimit({ bucket: "global", ...global })); app.use( @@ -150,17 +178,25 @@ export async function initRateLimits(app: Router) { bucket: "error", error: true, onlyIp: true, - ...error - }) + ...error, + }), ); app.use("/guilds/:id", rateLimit(routes.guild)); app.use("/webhooks/:id", rateLimit(routes.webhook)); app.use("/channels/:id", rateLimit(routes.channel)); app.use("/auth/login", rateLimit(routes.auth.login)); - app.use("/auth/register", rateLimit({ onlyIp: true, success: true, ...routes.auth.register })); + app.use( + "/auth/register", + rateLimit({ onlyIp: true, success: true, ...routes.auth.register }), + ); } -async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits: number; window: number; }) { +async function hitRoute(opts: { + executor_id: string; + bucket_id: string; + max_hits: number; + window: number; +}) { const id = opts.executor_id + opts.bucket_id; let limit = Cache.get(id); if (!limit) { @@ -169,7 +205,7 @@ async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits executor_id: opts.executor_id, expires_at: new Date(Date.now() + opts.window * 1000), hits: 0, - blocked: false + blocked: false, }; Cache.set(id, limit); } @@ -205,4 +241,4 @@ async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits } await ratelimit.save(); */ -} \ No newline at end of file +} diff --git a/src/api/middlewares/Translation.ts b/src/api/middlewares/Translation.ts index c0b7a4b8..05038040 100644 --- a/src/api/middlewares/Translation.ts +++ b/src/api/middlewares/Translation.ts @@ -9,8 +9,12 @@ const ASSET_FOLDER_PATH = path.join(__dirname, "..", "..", "..", "assets"); export async function initTranslation(router: Router) { const languages = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "locales")); - const namespaces = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "locales", "en")); - const ns = namespaces.filter((x) => x.endsWith(".json")).map((x) => x.slice(0, x.length - 5)); + const namespaces = fs.readdirSync( + path.join(ASSET_FOLDER_PATH, "locales", "en"), + ); + const ns = namespaces + .filter((x) => x.endsWith(".json")) + .map((x) => x.slice(0, x.length - 5)); await i18next .use(i18nextBackend) @@ -21,9 +25,11 @@ export async function initTranslation(router: Router) { fallbackLng: "en", ns, backend: { - loadPath: path.join(ASSET_FOLDER_PATH, "locales") + "/{{lng}}/{{ns}}.json", + loadPath: + path.join(ASSET_FOLDER_PATH, "locales") + + "/{{lng}}/{{ns}}.json", }, - load: "all" + load: "all", }); router.use(i18nextMiddleware.handle(i18next, {})); -- cgit 1.4.1