From c173f3c858324d085a6aac85760fb41e4079ea25 Mon Sep 17 00:00:00 2001 From: Kiko Beats Date: Mon, 15 Jan 2024 12:07:23 +0100 Subject: [PATCH] fix: ensure fallback url is reachable --- package.json | 2 +- src/avatar/resolve.js | 21 ++++++++++++--------- src/server.js | 6 ++++++ test/query-parameters.js | 15 +++++++++++++++ 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index d1b9815..50aaf13 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "@microlink/mql": "~0.12.2", "@microlink/ping-url": "~1.4.11", "@microlink/ua": "~1.2.0", + "async-memoize-one": "~1.1.6", "browserless": "~10.2.4", "cacheable-lookup": "~6.1.0", "cacheable-response": "~2.8.10", @@ -110,7 +111,6 @@ "is-email-like": "~1.0.0", "is-url-http": "~2.3.7", "lodash": "~4.17.21", - "memoize-one": "~6.0.0", "ms": "~2.1.3", "on-finished": "~2.4.1", "p-any": "~3.0.0", diff --git a/src/avatar/resolve.js b/src/avatar/resolve.js index af40788..d97052f 100644 --- a/src/avatar/resolve.js +++ b/src/avatar/resolve.js @@ -1,9 +1,10 @@ 'use strict' const debug = require('debug-logfmt')('unavatar:resolve') +const reachableUrl = require('../util/reachable-url') const isAbsoluteUrl = require('is-absolute-url') +const memoizeOne = require('async-memoize-one') const { getTtl } = require('../send/cache') -const memoizeOne = require('memoize-one') const isUrlHttp = require('is-url-http') const pTimeout = require('p-timeout') const pReflect = require('p-reflect') @@ -44,16 +45,18 @@ const optimizeUrl = async (url, query) => { }).toString()}` } -const getDefaultFallbackUrl = memoizeOne( - ({ protocol, host }) => `${protocol}://${host}/fallback.png` -) +const getDefaultFallbackUrl = ({ protocol, host }) => + this.url || (this.url = `${protocol}://${host}/fallback.png`) -const getFallbackUrl = memoizeOne(({ query, protocol, host }) => { +const getFallbackUrl = memoizeOne(async ({ query, protocol, host }) => { const { fallback } = query if (fallback === false) return null - - return isUrlHttp(fallback) && isAbsoluteUrl(fallback) - ? fallback + if (!isUrlHttp(fallback) || !isAbsoluteUrl(fallback)) { + return getDefaultFallbackUrl({ protocol, host }) + } + const { statusCode, url } = await reachableUrl(fallback) + return reachableUrl.isReachable({ statusCode }) + ? url : getDefaultFallbackUrl({ protocol, host }) }) @@ -74,7 +77,7 @@ module.exports = fn => async (req, res) => { } if (value === undefined) { - const data = getFallbackUrl({ query, protocol, host }) + const data = await getFallbackUrl({ query, protocol, host }) value = data ? { type: 'url', data } : null } diff --git a/src/server.js b/src/server.js index 56803ff..9b287d7 100644 --- a/src/server.js +++ b/src/server.js @@ -21,3 +21,9 @@ server.listen(PORT, () => { address: API_URL }) }) + +process.on('uncaughtException', error => { + debug.error('uncaughtException', { + requestUrl: error.response?.requestUrl + }) +}) diff --git a/test/query-parameters.js b/test/query-parameters.js index 3f912b0..e149c26 100644 --- a/test/query-parameters.js +++ b/test/query-parameters.js @@ -36,6 +36,21 @@ test('fallback', async t => { t.is(headers['content-type'], 'application/json; charset=utf-8') }) +test('fallback # use default value if fallback provided is not reachable', async t => { + const serverUrl = await runServer(t) + + const { headers, body } = await got( + 'github/__notexistprofile__?fallback=https://nexmoe.com/thisis404.png&json', + { + prefixUrl: serverUrl, + responseType: 'json' + } + ) + + t.is(body.url, `${serverUrl.toString()}fallback.png`) + t.is(headers['content-type'], 'application/json; charset=utf-8') +}) + test('ttl', t => { t.is(getTtl(), CACHE_TTL) t.is(getTtl(null), CACHE_TTL)