diff --git a/app/build/plugins/videoEmbeds/bandcamp.js b/app/build/plugins/videoEmbeds/bandcamp.js index c53cc56fe9c..24f64b6bcda 100644 --- a/app/build/plugins/videoEmbeds/bandcamp.js +++ b/app/build/plugins/videoEmbeds/bandcamp.js @@ -1,70 +1,47 @@ -var request = require("request"); -var cheerio = require("cheerio"); -var url = require("url"); +const fetch = require("node-fetch"); +const cheerio = require("cheerio"); +const { parse } = require("url"); -// we prepend a zero-width char because of a weird fucking -// bug on mobile safari where if the embed is the first child, -// the video player will not show. This causes issues with -// inline elements displaying (adds extra space) solution needed -// that doesn't disrupt page layout... -function template(url, width, height) { - return ( - '
' - ); -} +const ERROR_MESSAGE = "Could not retrieve song properties"; -var FAIL = "Could not retrieve song properties"; - -module.exports = function (href, callback) { - // Before loading anything, check that the path starts with - // /album or /track - var path, paths; +module.exports = async function (href, callback) { try { - path = url.parse(href).pathname; - } catch (e) { - return callback(new Error(FAIL)); - } - - // Trim trailing slash if applicable - if (path.slice(-1) === "/") path = path.slice(0, -1); - - paths = path.split("/"); - - if (paths.length < 1) { - return callback(new Error(FAIL)); - } + const { hostname, pathname } = parse(href); + const path = pathname.toLowerCase(); - var category = paths[1]; - if (category !== "track" && category !== "album") { - return callback(new Error(FAIL)); - } + if (!hostname.endsWith("bandcamp.com")) { + return callback(new Error(ERROR_MESSAGE)); + } - request(href, function (error, response, body) { - if (error) return callback(error); + if (!path || !path.match(/\/(album|track)/)) { + return callback(new Error(ERROR_MESSAGE)); + } - var $, height, width, html; + const res = await fetch(href); + const body = await res.text(); + const $ = cheerio.load(body); - try { - $ = cheerio.load(body); - width = Number($('meta[property="og:video:width"]').attr("content")); - height = Number($('meta[property="og:video:height"]').attr("content")); - html = $('meta[property="og:video"]').attr("content"); - } catch (error) { - return callback(new Error(FAIL)); - } + const width = Number($('meta[property="og:video:width"]').attr("content")); + const height = Number( + $('meta[property="og:video:height"]').attr("content") + ); + const html = $('meta[property="og:video"]').attr("content"); if (!html || isNaN(height) || isNaN(width)) { - return callback(new Error(FAIL)); + return callback(new Error(ERROR_MESSAGE)); } - return callback(null, template(html, width, height)); - }); + // we prepend a zero-width char because of a weird + // bug on mobile safari where if the embed is the first child, + // the video player will not show. This causes issues with + // inline elements displaying (adds extra space) solution needed + // that doesn't disrupt page layout... + const embedHTML = `
`; + + callback(null, embedHTML); + } catch (error) { + callback(new Error(ERROR_MESSAGE)); + } }; diff --git a/app/build/plugins/videoEmbeds/tests.js b/app/build/plugins/videoEmbeds/tests.js deleted file mode 100644 index 5aba7a8aada..00000000000 --- a/app/build/plugins/videoEmbeds/tests.js +++ /dev/null @@ -1,61 +0,0 @@ -describe("video-embed plugin", function () { - var videoEmbed = require("./index"); - var cheerio = require("cheerio"); - - function is(html, expected) { - it("handles " + html.slice(0, 15), function (done) { - var $ = cheerio.load(html, { decodeEntities: false }); - - var callback = function (output) { - expect($.html()).toEqual(expected); - done(); - }; - - videoEmbed.render($, callback); - }); - } - - function link(url) { - return '' + url + ""; - } - - var wideVideo = "https://www.youtube.com/watch?v=YaT_5KoGh1Q"; - var exp = - '
'; - - // is("
" + link(wideVideo) + "
", exp); - // is(link(wideVideo), exp); - // is( - // "

" + link(wideVideo) + "

", - // '
' - // ); - // is( - // "

Hello: " + link(wideVideo) + " bye...

", - // '

Hello: bye...

' - // ); - - var vid2 = "https://www.youtube.com/watch?v=MJ62hh0a9U4"; - var vid2Alt = "http://youtube.com/watch?v=MJ62hh0a9U4"; - - var exp2 = - '
'; - - // is('' + vid2 + "", exp2); - // is('' + vid2Alt + "", exp2); - - var vimeoVid = "https://vimeo.com/87952436"; - var exp3 = - '
'; - - // is(link(vimeoVid), exp3); - - var bad1 = "http://foo.com"; - var bad2 = "https://www.youtube.com/watch?v=*(&*^%"; - var bad3 = "http://youtube.com/watch"; - var bad4 = "https://vimeo.com/^*%&^*("; - - // is(link(bad1), link(bad1)); - // is(link(bad2), link(bad2)); - // is(link(bad3), link(bad3)); - // is(link(bad4), link(bad4)); -}); diff --git a/app/build/plugins/videoEmbeds/tests/bandcamp.js b/app/build/plugins/videoEmbeds/tests/bandcamp.js new file mode 100644 index 00000000000..3abdb043816 --- /dev/null +++ b/app/build/plugins/videoEmbeds/tests/bandcamp.js @@ -0,0 +1,52 @@ +describe("bandcamp embeds", function () { + const bandcamp = require("../bandcamp"); + + it("handles an empty href", function (done) { + const href = ""; + bandcamp(href, (err, template) => { + expect(err.message).toEqual("Could not retrieve song properties"); + expect(template).toEqual(undefined); + done(); + }); + }); + + it("handles an invalid href", function (done) { + const href = "https://bandcamp.com"; + bandcamp(href, (err, template) => { + expect(err.message).toEqual("Could not retrieve song properties"); + expect(template).toEqual(undefined); + done(); + }); + }); + + it("handles an invalid path", function (done) { + const href = "https://bandcamp.com/invalid"; + bandcamp(href, (err, template) => { + expect(err.message).toEqual("Could not retrieve song properties"); + expect(template).toEqual(undefined); + done(); + }); + }); + + it("handles a valid link to an album on a bandcamp subdomain", function (done) { + const href = "https://oliviachaney.bandcamp.com/album/circus-of-desire"; + bandcamp(href, (err, template) => { + expect(err).toEqual(null); + expect(template).toEqual( + `
` + ); + done(); + }); + }); + + it("handles a valid link to a track on a bandcamp subdomain", function (done) { + const href = "https://cloquet.bandcamp.com/track/new-drugs"; + bandcamp(href, (err, template) => { + expect(err).toEqual(null); + expect(template).toEqual( + `
` + ); + done(); + }); + }); +}); diff --git a/app/build/plugins/videoEmbeds/tests/index.js b/app/build/plugins/videoEmbeds/tests/index.js new file mode 100644 index 00000000000..e273956cc4d --- /dev/null +++ b/app/build/plugins/videoEmbeds/tests/index.js @@ -0,0 +1,20 @@ +describe("video-embed plugin", function () { + const videoEmbed = require("../index"); + const cheerio = require("cheerio"); + + it("embeds videos from youtube, vimeo and music from bandcamp", function (done) { + const $ = cheerio.load( + `https://www.youtube.com/watch?v=MJ62hh0a9U4 + https://vimeo.com/87952436 + https://oliviachaney.bandcamp.com/album/circus-of-desire + https://cloquet.bandcamp.com/track/new-drugs + https://foo.com/87952436 + ` + ); + videoEmbed.render($, function (err) { + expect(err).toEqual(null); + expect($("iframe").length).toEqual(4); + done(); + }); + }); +}); diff --git a/app/build/plugins/videoEmbeds/tests/vimeo.js b/app/build/plugins/videoEmbeds/tests/vimeo.js new file mode 100644 index 00000000000..c916660443b --- /dev/null +++ b/app/build/plugins/videoEmbeds/tests/vimeo.js @@ -0,0 +1,82 @@ +describe("vimeo embeds", function () { + const vimeo = require("../vimeo"); + it("handles an empty href", function (done) { + const href = ""; + vimeo(href, (err, template) => { + expect(err.message).toEqual("Could not retrieve video properties"); + expect(template).toEqual(undefined); + done(); + }); + }); + + it("handles an invalid href", function (done) { + const href = "https://vimeo.com/^*%&^*("; + vimeo(href, (err, template) => { + expect(err.message).toEqual("Could not retrieve video properties"); + expect(template).toEqual(undefined); + done(); + }); + }); + + it("handles an invalid host", function (done) { + const href = "https://viimeo.com/123"; + vimeo(href, (err, template) => { + expect(err.message).toEqual("Could not retrieve video properties"); + expect(template).toEqual(undefined); + done(); + }); + }); + + it("handles an invalid path", function (done) { + const href = "https://vimeo.com/invalid"; + vimeo(href, (err, template) => { + expect(err.message).toEqual("Could not retrieve video properties"); + expect(template).toEqual(undefined); + done(); + }); + }); + + it("handles a valid link", function (done) { + const href = "https://vimeo.com/87952436"; + vimeo(href, (err, template) => { + expect(err).toEqual(null); + expect(template).toEqual( + `
` + ); + done(); + }); + }); + + it("handles a valid link with a trailing slash", function (done) { + const href = "https://vimeo.com/87952436/"; + vimeo(href, (err, template) => { + expect(err).toEqual(null); + expect(template).toEqual( + `
` + ); + done(); + }); + }); + + it("handles a valid link with a query string", function (done) { + const href = "https://vimeo.com/87952436?query=string"; + vimeo(href, (err, template) => { + expect(err).toEqual(null); + expect(template).toEqual( + `
` + ); + done(); + }); + }); + + it("handles a valid link with a hash", function (done) { + const href = "https://vimeo.com/87952436#hash"; + vimeo(href, (err, template) => { + expect(err).toEqual(null); + expect(template).toEqual( + `
` + ); + done(); + }); + }); +}); diff --git a/app/build/plugins/videoEmbeds/tests/youtube.js b/app/build/plugins/videoEmbeds/tests/youtube.js new file mode 100644 index 00000000000..70e8e11f6a3 --- /dev/null +++ b/app/build/plugins/videoEmbeds/tests/youtube.js @@ -0,0 +1,41 @@ +describe("youtube embeds", function () { + const youtube = require("../youtube"); + + it("handles an empty href", function (done) { + const href = ""; + youtube(href, (err, template) => { + expect(err.message).toEqual("Could not parse youtube video"); + expect(template).toEqual(undefined); + done(); + }); + }); + + it("handles an invalid href", function (done) { + const href = "https://www.youtube.com/watch?v=*(&*^%"; + youtube(href, (err, template) => { + expect(err.message).toEqual("Could not parse youtube video"); + expect(template).toEqual(undefined); + done(); + }); + }); + + it("handles an invalid host", function (done) { + const href = "https://www.youtubee.com/watch?v=123"; + youtube(href, (err, template) => { + expect(err.message).toEqual("Could not parse youtube video"); + expect(template).toEqual(undefined); + done(); + }); + }); + + it("handles a valid link", function (done) { + const href = "https://www.youtube.com/watch?v=YaT_5KoGh1Q"; + youtube(href, (err, template) => { + expect(err).toEqual(null); + expect(template).toEqual( + `
` + ); + done(); + }); + }); +}); diff --git a/app/build/plugins/videoEmbeds/vimeo.js b/app/build/plugins/videoEmbeds/vimeo.js index 34d6a29f0c2..ac42730461e 100644 --- a/app/build/plugins/videoEmbeds/vimeo.js +++ b/app/build/plugins/videoEmbeds/vimeo.js @@ -1,63 +1,40 @@ -var request = require("request"); -var basename = require("path").basename; - -// we prepend a zero-width char because of a weird fucking -// bug on mobile safari where if the embed is the first child, -// the video player will not show. This causes issues with -// inline elements displaying (adds extra space) solution needed -// that doesn't disrupt page layout... -function template(id, ratio, thumbnail) { - return ( - '
' - ); -} - -function apiURL(id) { - return "https://vimeo.com/api/v2/video/" + id + ".json"; -} +const fetch = require("node-fetch"); +const { parse } = require("url"); var FAIL = "Could not retrieve video properties"; -module.exports = function (href, callback) { - var id, height, width, ratio, el, thumbnail; - +module.exports = async function (href, callback) { try { - // we trim because without it, - // some text editors fuck up. - id = basename(href).trim(); + const { hostname, pathname } = parse(href); + + if (!hostname || !hostname.match(/vimeo.com$/)) throw new Error(FAIL); + + // parse the video id from the url, even if it has a trailing slash + const id = pathname.match(/\/(\d+)\/?$/)[1]; + + if (!id) throw new Error(FAIL); + + const res = await fetch("https://vimeo.com/api/v2/video/" + id + ".json"); + const body = await res.json(); + + const el = body[0]; + + if (!el || !el.width || !el.height) throw new Error(FAIL); + + const thumbnail = el.thumbnail_large + ".jpg"; + const height = el.height; + const width = el.width; + const ratio = (height / width) * 100; + + // we prepend a zero-width char because of a weird fucking + // bug on mobile safari where if the embed is the first child, + // the video player will not show. This causes issues with + // inline elements displaying (adds extra space) solution needed + // that doesn't disrupt page layout... + const embedHTML = `
`; + + return callback(null, embedHTML); } catch (e) { return callback(new Error(FAIL)); } - - // The request module has a known bug - // which leaks memory and event emitters - // during redirects. We cap the maximum - // redirects to 5 to avoid encountering - // errors when it creates 10+ emitters - // for a URL with 10+ redirects... - var options = { - url: apiURL(id), - json: true, - maxRedirects: 5, - }; - - request(options, function (error, response, body) { - if (error || !body) return callback(new Error(FAIL)); - - el = body[0]; - - if (!el || !el.width || !el.height) return callback(new Error(FAIL)); - thumbnail = el.thumbnail_large + ".jpg"; - height = el.height; - width = el.width; - ratio = (height / width) * 100; - - return callback(null, template(id, ratio, thumbnail)); - }); }; diff --git a/app/build/plugins/videoEmbeds/youtube.js b/app/build/plugins/videoEmbeds/youtube.js index 808c374bda1..94d11dbcc58 100644 --- a/app/build/plugins/videoEmbeds/youtube.js +++ b/app/build/plugins/videoEmbeds/youtube.js @@ -1,93 +1,57 @@ -var request = require("request"); -var Url = require("url"); -var config = require("config"); -var cheerio = require("cheerio"); - -var PLAYER_OPTIONS = "rel=0&wmode=transparent&rel=0&autohide=1&showinfo=0"; - -// we prepend a zero-width char because of a bug on mobile safari -// where if the embed is the first child, -// the video player will not show. -function template (id, ratio) { - return ( - '
' - ); -} - -function apiURL (id) { - return ( - "https://www.googleapis.com/youtube/v3/videos?part=player&id=" + - id + - "&key=" + - config.youtube.secret - ); -} - -function fail () { - return new Error("Could not parse youtube video"); -} +const fetch = require("node-fetch"); +const { parse } = require("url"); +const config = require("config"); +const cheerio = require("cheerio"); +const querystring = require("querystring"); +const ERROR_MESSAGE = "Could not parse youtube video"; + +module.exports = async function (href, callback) { + try { + const { query, hostname, pathname } = parse(href); -function extractID (href) { - var url = Url.parse(href, true); - var id; - if (url.host === "youtu.be") { - id = url.pathname.slice(1); - } else { - id = url.query.v; - } + if (!hostname || !hostname.match(/youtube.com$|youtu.be$/)) { + throw new Error(ERROR_MESSAGE); + } - // Url.parse maps backslashes (used to escape) to forward - // slashes, e.g. '/_' for some reason. We remove the forward - // slash here, since it breaks video embeds for videos with - // ids that contain escapable characters, e.g. - // https://youtu.be/6orc\_lHvJKY - id = id.split("/").join(""); + // Url.parse maps backslashes (used to escape) to forward + // slashes, e.g. '/_' for some reason. We remove the forward + // slash here, since it breaks video embeds for videos with + // ids that contain escapable characters, e.g. + // https://youtu.be/6orc\_lHvJKY + const id = + hostname === "youtu.be" + ? pathname.slice(1).replace(/\\/g, "") + : querystring.parse(query).v; - return id; -} + if (!id) throw new Error(ERROR_MESSAGE); -module.exports = function (href, callback) { - var id, width, height, ratio, items, player; + // default ratio for youtube videos + let ratio = 56.25; - try { - id = extractID(href); - } catch (e) { - return callback(fail()); - } - - if (!id) return callback(fail()); + try { + const res = await fetch( + "https://www.googleapis.com/youtube/v3/videos?part=player&id=" + + id + + "&key=" + + config.youtube.secret + ); - // The request module has a known bug - // which leaks memory and event emitters - // during redirects. We cap the maximum - // redirects to 5 to avoid encountering - // errors when it creates 10+ emitters - // for a URL with 10+ redirects... - var options = { - url: apiURL(id), - json: true, - maxRedirects: 5 - }; + const body = await res.json(); - request(options, function (error, response, body) { - try { var $ = cheerio.load(body.items[0].player.embedHtml); - width = $("iframe").attr("width"); - height = $("iframe").attr("height"); + const width = $("iframe").attr("width"); + const height = $("iframe").attr("height"); ratio = (height / width) * 100; - } catch (e) { - // Fallback to default Youtube player ratio if API - // request fails or shape of data returned changes - ratio = 56.5; - } + } catch (e) {} + + // we prepend a zero-width char because of a bug on mobile safari + // where if the embed is the first child, + // the video player will not show. + const embedHTML = `
`; - return callback(null, template(id, ratio)); - }); + return callback(null, embedHTML); + } catch (e) { + return callback(new Error(ERROR_MESSAGE)); + } }; diff --git a/app/dashboard/account/add-new-site.js b/app/dashboard/account/add-new-site.js index 93e7432cfa9..6de3d42e1b8 100644 --- a/app/dashboard/account/add-new-site.js +++ b/app/dashboard/account/add-new-site.js @@ -4,8 +4,7 @@ var Blog = require("models/blog"); var _ = require("lodash"); var prettyPrice = require("helper/prettyPrice"); var config = require("config"); -var request = require("request"); -var config = require("config"); +var fetch = require("node-fetch"); var stripe = require("stripe")(config.stripe.secret); var User = require("models/user"); var Email = require("helper/email"); @@ -239,7 +238,11 @@ function saveBlog (req, res, next) { } // Begin SSL cert fetching process - request(Blog.extend(blog).url, function () {}); + // in the background + fetch(Blog.extend(blog).url) + .then(function () {}) + .catch(function () {}); + req.blog = blog; next(); }); diff --git a/app/dashboard/import/sources/arena/posts.js b/app/dashboard/import/sources/arena/posts.js index 41347d7a2bf..6b7dbd88f5d 100644 --- a/app/dashboard/import/sources/arena/posts.js +++ b/app/dashboard/import/sources/arena/posts.js @@ -1,42 +1,35 @@ -const request = require("request"); +const fetch = require("node-fetch"); const PAGE_SIZE = 100; -module.exports = function posts({ slug, status }) { - return new Promise((resolve, reject) => { - var page = 0; - var posts = []; - var new_posts; +module.exports = async function posts ({ slug, status }) { + let page = 0; + let posts = []; + let new_posts; + async function fetchPage (page) { + const url = base(slug, page); status(`Fetching page ${page + 1} of channel`); - request(base(slug, page), function with_page(err, body, res) { - if (err) return reject(err); - - new_posts = JSON.parse(res).contents; - - posts = posts.concat(new_posts); - - if (!new_posts.length) { - status(`Fetched everything on channel`); - return resolve(posts); - } - - page++; - - status(`Fetching page ${page + 1} of channel`); - request(base(slug, page), with_page); - }); - }); + const response = await fetch(url); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); + return data.contents; + } + + new_posts = await fetchPage(page); + posts = posts.concat(new_posts); + page++; + while (new_posts.length === PAGE_SIZE) { + new_posts = await fetchPage(page); + posts = posts.concat(new_posts); + page++; + } + + status(`Fetched everything on channel`); + return posts; }; -function base(slug, page) { - return ( - "https://api.are.na/v2/channels/" + - slug + - "/contents?direction=desc&sort=position&per=" + - PAGE_SIZE + - "&channel_slug=" + - slug + - "&page=" + - page - ); +function base (slug, page) { + return `https://api.are.na/v2/channels/${slug}/contents?direction=desc&sort=position&per=${PAGE_SIZE}&channel_slug=${slug}&page=${page}`; } diff --git a/app/dashboard/import/sources/feed/load.js b/app/dashboard/import/sources/feed/load.js index 06291f06b0f..bac693fd293 100644 --- a/app/dashboard/import/sources/feed/load.js +++ b/app/dashboard/import/sources/feed/load.js @@ -1,28 +1,19 @@ -var cheerio = require("cheerio"); -var request = require("request"); +const fetch = require("node-fetch"); +const cheerio = require("cheerio"); -module.exports = function load(feed_url, callback) { - if (!callback) throw new Error("Please pass a callback"); +module.exports = async function load (feed_url) { + if (!feed_url) throw new Error("Please pass a URL to an RSS feed"); - if (!feed_url) return callback(new Error("Please pass a URL to an RSS feed")); + const response = await fetch(feed_url); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const body = await response.text(); - request(feed_url, function (err, res, body) { - if (err) return callback(err); - - var $ = cheerio.load(body, { - // This prevent cheerio from replacing characters - // it really ought to preserve. - decodeEntities: false, - - // Enabling XML mode has confusing effects - // 1. It makes it hard to read certain non-standard - // tags that the evernote file contains, like - // 2. It allows us to read the contents of the tags - // without manually removing the CDATA tags. So be - // careful if you remove this. - xmlMode: true, - }); - - callback(null, $); + const $ = cheerio.load(body, { + decodeEntities: false, + xmlMode: true }); + + return $; }; diff --git a/app/dashboard/import/sources/pdr/extract_article.js b/app/dashboard/import/sources/pdr/extract_article.js deleted file mode 100644 index 4a204334129..00000000000 --- a/app/dashboard/import/sources/pdr/extract_article.js +++ /dev/null @@ -1,33 +0,0 @@ -var readability = require("node-readability"); -var EventEmitter = require("events"); -var MyEmitter = new EventEmitter(); - -module.exports = function (article_url, callback) { - var has_responded = false; - var content; - var html; - var title; - var label = "done" + article_url; - - MyEmitter.on(label, function () { - callback(null, title, content, html); - }); - - // This seems to swallow errors for whole process? - // WTF! - - readability(article_url, function (err, article) { - if (has_responded) return; - - has_responded = true; - - if (err) return callback(err); - - content = article.content.slice(); - html = article.cache.body.slice(); - title = article.title.slice(); - - MyEmitter.emit(label); - article.close(); - }); -}; diff --git a/app/dashboard/import/sources/pdr/extract_author.js b/app/dashboard/import/sources/pdr/extract_author.js deleted file mode 100644 index e1b00b123e8..00000000000 --- a/app/dashboard/import/sources/pdr/extract_author.js +++ /dev/null @@ -1,47 +0,0 @@ -module.exports = function ($) { - console.log($.html()); - - var candidates = []; - var best_guess; - - function calculate_score(el) { - var score = 0; - - var text = $(el).text(); - var number_of_words = text.split(" ").length; - var has_numbers = text.match(/\d+/); - var next_node_starts_with_is = - el.next && - el.next.type === "text" && - el.next.data.trim().toLowerCase().indexOf("is") === 0; - - // console.log(text, {next_node_starts_with_is: next_node_starts_with_is, has_numbers: has_numbers, number_of_words: number_of_words}); - if (!el.prev) score++; - if (number_of_words === 2) score++; - if (next_node_starts_with_is) score++; - if (number_of_words > 5) score--; - if (has_numbers) score--; - - return score; - } - - $("hr + p strong") - .last() - .add($("hr + p span strong").last()) - .add($("p span strong").last()) - .add($("p em")) - .add($("p strong")) - .each(function (i, el) { - candidates.push({ text: $(el).text(), score: calculate_score(el) }); - }); - - console.log(candidates); - - best_guess = candidates.reduce(function (sum, value) { - return sum.score > value.score ? sum : value; - }, candidates[0]); - - console.log("BEST GUESS", best_guess.text); - - return best_guess.text; -}; diff --git a/app/dashboard/import/sources/pdr/extract_summary.js b/app/dashboard/import/sources/pdr/extract_summary.js deleted file mode 100644 index ee11e639c17..00000000000 --- a/app/dashboard/import/sources/pdr/extract_summary.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function ($) { - return $("p").first().text().trim(); -}; diff --git a/app/dashboard/import/sources/pdr/extract_tags.js b/app/dashboard/import/sources/pdr/extract_tags.js deleted file mode 100644 index f6778283ba9..00000000000 --- a/app/dashboard/import/sources/pdr/extract_tags.js +++ /dev/null @@ -1,31 +0,0 @@ -var cheerio = require("cheerio"); - -module.exports = function (html) { - var $ = cheerio.load(html, { decodeEntities: false }); - var class_names = $(".hpy_single_article article").first().attr("class"); - - var tags = class_names - .split(" ") - .filter(function (name) { - name = name.trim().toLowerCase(); - - return ( - name && - name.indexOf("category-") === 0 && - name.indexOf("featured") === -1 && - name.indexOf("article") === -1 - ); - }) - .map(function (name) { - name = name.trim().toLowerCase().slice("category-".length); - name = name.split("-").join(" "); - name = name[0].toUpperCase() + name.slice(1); - - if (name === "Science medicine") name = "Science"; - if (name === "Art and illustrations") name = "Art"; - - return name; - }); - - return tags; -}; diff --git a/app/dashboard/import/sources/pdr/find_thumbnail.js b/app/dashboard/import/sources/pdr/find_thumbnail.js deleted file mode 100644 index 5880632ebd2..00000000000 --- a/app/dashboard/import/sources/pdr/find_thumbnail.js +++ /dev/null @@ -1,28 +0,0 @@ -var thumbnails = require("fs-extra") - .readFileSync(__dirname + "/thumbnails.txt", "utf-8") - .trim() - .split("\n"); - -module.exports = function ($) { - var thumbnail, src; - - // Fix bare URLs in footnotes - $("img").each(function (i, el) { - src = $(el).attr("src"); - - if (thumbnails.indexOf(src) > -1 && !!thumbnail) { - console.log(thumbnail); - console.log(src); - throw new Error( - "Two valid thumbnails for a given entry, remove one of them" - ); - } - - if (thumbnails.indexOf(src) > -1 && !thumbnail) { - thumbnail = src; - console.log("FOUND A THUMBNAIl", thumbnail); - } - }); - - return thumbnail; -}; diff --git a/app/dashboard/import/sources/pdr/fix_azon.js b/app/dashboard/import/sources/pdr/fix_azon.js deleted file mode 100644 index b939eaf288c..00000000000 --- a/app/dashboard/import/sources/pdr/fix_azon.js +++ /dev/null @@ -1,58 +0,0 @@ -module.exports = function ($) { - $("p").each(function (i, el) { - if ($(el).text().indexOf("Discover more recommended books") > -1) - $(el).remove(); - if ($(el).text().indexOf("[easyazon_link") > -1) $(el).remove(); - }); - - $(".bookinfo").each(function (i, el) { - var text = ""; - - // Remove empty p tags - $(el) - .find("p") - .each(function (i, el) { - if (!$(el).html().trim()) $(el).remove(); - }); - - $(el) - .find("span") - .each(function (i, el) { - text += $(el).text(); - - $(el).remove(); - }); - - text += $(el).find(".bookauthor").first().text(); - - $(el).find(".bookauthor").remove(); - - $(el).prepend("

" + text + "

"); - }); - - $("a").each(function (i, el) { - $(el) - .contents() - .each(function fix(i, el) { - if (el.type !== "text") return $(el).contents().each(fix); - - var text = el.data; - - // [easyazon_link identifier=“191001009X” locale=“US” tag=“thepubdomrev-20”] - if (text.indexOf("[easyazon_link") === -1) return; - - console.log("BEFORE", text); - - var start = text.indexOf("[easyazon_link"); - var end = text.slice(start).indexOf("]") + start + 1; - - console.log(start, end); - - text = text.slice(0, start) + text.slice(end); - - console.log("AFTER", text); - - el.data = text; - }); - }); -}; diff --git a/app/dashboard/import/sources/pdr/fix_broken_images.js b/app/dashboard/import/sources/pdr/fix_broken_images.js deleted file mode 100644 index d99b56acbf2..00000000000 --- a/app/dashboard/import/sources/pdr/fix_broken_images.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = function ($) { - $("img").each(function (i, el) { - var html = $.html(el); - - if (html.indexOf("=") === -1) return; - - var new_el = $(""); - - for (var x in el.attribs) if (x !== '"') new_el.attr(x, el.attribs[x]); - - $(el).replaceWith(new_el); - }); -}; diff --git a/app/dashboard/import/sources/pdr/fix_empty_public_domain_works.js b/app/dashboard/import/sources/pdr/fix_empty_public_domain_works.js deleted file mode 100644 index 7db6eda79cd..00000000000 --- a/app/dashboard/import/sources/pdr/fix_empty_public_domain_works.js +++ /dev/null @@ -1,43 +0,0 @@ -module.exports = function ($) { - // This was a specific issue with - // https://publicdomainreview.org/2017/05/31/gustav-wunderwalds-paintings-of-weimar-berlin/ - // and produced markdown that pandoc couldn't parse - $("p strong br").remove(); - - var has_upper_case = false; - var has_lower_case = false; - - $("p").each(function (i, el) { - if ($(el).text() === "PUBLIC DOMAIN WORKS") has_upper_case = true; - - if ($(el).text().toLowerCase() === "public domain works" && !has_upper_case) - has_lower_case = true; - }); - - if (has_upper_case && has_upper_case) { - $("p").each(function (i, el) { - if ($(el).text() === "PUBLIC DOMAIN WORKS") $(el).remove(); - }); - } - - // This papers over a bug with readability which clipped - // the last list in the article. - $("p").each(function (i, el) { - var text = $(el).text(); - - if (text !== "Public Domain Works") return; - - var next_text = ""; - - $(el) - .nextAll() - .each(function (i, el) { - next_text += $(el).text().trim(); - }); - - if (!next_text) { - $(el).prevUntil("hr").prev().remove(); - $(el).remove(); - } - }); -}; diff --git a/app/dashboard/import/sources/pdr/index.js b/app/dashboard/import/sources/pdr/index.js deleted file mode 100644 index f85d56c0cbe..00000000000 --- a/app/dashboard/import/sources/pdr/index.js +++ /dev/null @@ -1,31 +0,0 @@ -var load = require("./load"); -var parse = require("./parse"); -var fs = require("fs-extra"); - -if (require.main === module) - main(process.argv[2], function (err) { - if (err) throw err; - - process.exit(); - }); - -function main(output_directory, callback) { - if (!callback) throw new Error("Please pass a callback"); - - if (output_directory) - return callback( - new Error("Please pass an output directory as the second argument") - ); - - load(function (err, blog) { - if (err) return callback(err); - - fs.emptyDir(output_directory, function (err) { - if (err) return callback(err); - - parse(output_directory, blog, callback); - }); - }); -} - -module.exports = main; diff --git a/app/dashboard/import/sources/pdr/insert_disclaimer.js b/app/dashboard/import/sources/pdr/insert_disclaimer.js deleted file mode 100644 index 6127a2a8769..00000000000 --- a/app/dashboard/import/sources/pdr/insert_disclaimer.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = function (text) { - return ( - "{|<} *This essay was originally published in [The Public Domain Review](http://publicdomainreview.org/) under a Creative Commons License. Please see [their rules for reuse](http://publicdomainreview.org/legal/).*\n\n" + - text - ); -}; diff --git a/app/dashboard/import/sources/pdr/load.js b/app/dashboard/import/sources/pdr/load.js deleted file mode 100644 index 2a5463416cd..00000000000 --- a/app/dashboard/import/sources/pdr/load.js +++ /dev/null @@ -1,103 +0,0 @@ -var cheerio = require("cheerio"); -var request = require("request"); -var fs = require("fs-extra"); -var extract_article = require("./extract_article"); -var for_each = require("dashboard/importer/helper").for_each; - -if (require.main === module) { - var output_file = process.argv[2]; - - if (!output_file) - throw new Error("Please pass filename to write links to as first argument"); - - main(function (err, blog) { - if (err) throw err; - - fs.outputJson(output_file, blog, { spaces: 2 }, function (err) { - if (err) throw err; - - process.exit(); - }); - }); -} - -function main(callback) { - var page_no = 1; - - // For some reason these show on the homepage but not the essay page - // so we hard code them here for now... - var articles = [ - "http://publicdomainreview.org/2018/04/04/fallen-angels-birds-of-paradise-in-early-modern-europe/", - "http://publicdomainreview.org/2018/04/18/made-in-taiwan-how-a-frenchman-fooled-18th-century-london/", - ]; - - var base_url = "http://publicdomainreview.org/essays/page/"; - - console.log("Retrieving articles..."); - - request(base_url + page_no + "/", function then(err, res, body) { - if (err) return callback(err); - - var has_more = false; - var $ = cheerio.load(body, { - // This prevent cheerio from replacing characters - // it really ought to preserve. - decodeEntities: false, - - // Enabling XML mode has confusing effects - // 1. It makes it hard to read certain non-standard - // tags that the evernote file contains, like - // 2. It allows us to read the contents of the tags - // without manually removing the CDATA tags. So be - // careful if you remove this. - // xmlMode: true - }); - - $("article").each(function (i, el) { - articles.push($(el).find("a").first().attr("href")); - }); - - console.log("... Found", articles.length, "articles..."); - - has_more = $(".link-last a").text().trim() === "Next »"; - - if (has_more) return request(base_url + ++page_no + "/", then); - - console.log("Fetching article content..."); - - var blog = { - title: "Public Domain Review", - host: "publicdomainreview.org", - posts: [], - }; - - var called = 0; - var total = articles.length; - - for_each.multi(5)( - articles, - function (article_url, next) { - console.log("...", ++called, "/", total, article_url); - - extract_article(article_url, function (err, title, content, html) { - // third arg: meta - - blog.posts.push({ - title: title, - content: content, - html: html, - url: article_url, - }); - - next(); - }); - }, - function () { - console.log("Done!"); - callback(null, blog); - } - ); - }); -} - -module.exports = main; diff --git a/app/dashboard/import/sources/pdr/package.json b/app/dashboard/import/sources/pdr/package.json deleted file mode 100644 index d9a284c5eb9..00000000000 --- a/app/dashboard/import/sources/pdr/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "pdr", - "version": "1.0.0", - "main": "index.js", - "license": "CC0", - "dependencies": { - "cheerio": "^1.0.0-rc.2", - "fs-extra": "^5.0.0", - "node-readability": "^3.0.0", - "pretty": "^2.0.0", - "request": "^2.85.0" - } -} diff --git a/app/dashboard/import/sources/pdr/parse.js b/app/dashboard/import/sources/pdr/parse.js deleted file mode 100644 index 8fa887dd596..00000000000 --- a/app/dashboard/import/sources/pdr/parse.js +++ /dev/null @@ -1,147 +0,0 @@ -var fs = require("fs-extra"); -var cheerio = require("cheerio"); -var helper = require("dashboard/importer/helper"); - -var insert_video_embeds = helper.insert_video_embeds; -var determine_path = helper.determine_path; -var download_images = helper.download_images; -var insert_metadata = helper.insert_metadata; -var to_markdown = helper.to_markdown; -var for_each = helper.for_each; - -var extract_tags = require("./extract_tags"); -var extract_summary = require("./extract_summary"); -var find_thumbnail = require("./find_thumbnail"); -var insert_disclaimer = require("./insert_disclaimer"); -var tidy_figures = require("./tidy_figures"); -var fix_azon = require("./fix_azon"); -var fix_empty_public_domain_works = require("./fix_empty_public_domain_works"); -var fix_broken_images = require("./fix_broken_images"); -var tidy_footnotes = require("./tidy_footnotes"); -var relative_links = require("./relative_links"); - -function main(output_directory, blog, callback) { - var done = 0; - - for_each( - blog.posts, - function (post, next) { - // console.log(post.title, post.url); - - var created, updated; - var dateStamp, tags, draft, page, metadata, summary; - var $, content, title, html, url; - - content = post.content; - title = post.title; - html = post.html; - url = post.url; - - var date_string = url.split("/").slice(3, 6).join("/"); - - created = updated = dateStamp = new Date(date_string).valueOf(); - draft = page = false; - metadata = {}; - tags = extract_tags(html); - - content = insert_video_embeds(content); - - $ = cheerio.load(content, { decodeEntities: false }); - - // author = extract_author($); - // metadata.author = author || 'Adam Green'; - - tidy_figures($); - tidy_footnotes($); - fix_azon($); - fix_empty_public_domain_works($); - fix_broken_images($); - relative_links($, url); - - summary = extract_summary($); - content = $.html(); - - post = { - draft: false, - page: false, - - // We don't know any of these properties - // as far as I can tell. - name: "", - permalink: "", - summary: summary, - html: content, - title: title, - - dateStamp: dateStamp, - created: created, - updated: updated, - tags: tags, - metadata: metadata, - - // Clean up the contents of the - // tag. Evernote has quite a lot of cruft. - // Then convert into Markdown! - content: content, - }; - - post = determine_path(post); - - download_images(post, function (err, post) { - if (err) throw err; - - $ = cheerio.load(post.html, { decodeEntities: false }); - - if (find_thumbnail($)) metadata.thumbnail = find_thumbnail($); - - post.content = to_markdown(post.html); - post.content = insert_disclaimer(post.content); - - post = insert_metadata(post); - - console.log(++done + "/" + blog.posts.length, "...", post.path); - fs.outputFile(post.path, post.content, function (err) { - if (err) return callback(err); - - next(); - }); - }); - }, - function () { - callback(null, blog); - } - ); -} - -if (require.main === module) { - var input_file = process.argv[2]; - var output_directory = process.argv[3]; - var filter = process.argv[4]; - - if (!input_file) - throw new Error("Please pass filename to read links to as first argument"); - if (!output_directory) - throw new Error("Please pass output directory as second argument"); - - var blog = fs.readJsonSync(input_file); - - // console.log('!!!!! generating trimmed version of site'); - // blog.posts = blog.posts.slice(0, 100); - - if (filter) - blog.posts = blog.posts.filter(function (post) { - return post.url.trim().toLowerCase().indexOf(filter) > -1; - }); - - fs.emptyDir(output_directory, function (err) { - if (err) throw err; - - main(output_directory, blog, function (err) { - if (err) throw err; - - console.log("Complete!"); - }); - }); -} - -module.exports = main; diff --git a/app/dashboard/import/sources/pdr/relative_links.js b/app/dashboard/import/sources/pdr/relative_links.js deleted file mode 100644 index 56d61c2968e..00000000000 --- a/app/dashboard/import/sources/pdr/relative_links.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = function ($, url) { - $("a").each(function (i, el) { - var href = $(el).attr("href"); - - if (href && href.indexOf(url) === 0) { - $(el).attr("href", href.slice(url.length)); - } - }); -}; diff --git a/app/dashboard/import/sources/pdr/sources.json b/app/dashboard/import/sources/pdr/sources.json deleted file mode 100644 index 4ee25105f60..00000000000 --- a/app/dashboard/import/sources/pdr/sources.json +++ /dev/null @@ -1,156 +0,0 @@ -{ - "www.loc.gov": "Library of Congress", - "docnum.u-strasbg.fr": "L\"Université de Strasbourg", - "commons.wikimedia.org": "Wikimedia Commons", - "wellcomecollection.org": "Wellcome Library", - "www.biodiversitylibrary.org": "Biodiversity Library", - "archive.org": "Internet Archive", - "www.wdl.org": "World Digital Library Home", - "daten.digitale-sammlungen.de": "", - "johannes.library.manchester.ac.uk:8181": "Manchester City Library", - "library.princeton.edu": "Princeton University Library", - "www.metmuseum.org": "The Metropolitan Museum of Art", - "newberrylibrary.tumblr.com": "The Newberry", - "digitallibrary.tulane.edu": "Tulane University Digital Library", - "gallica.bnf.fr": "Gallica", - "images.bnf.fr": "Consultation de la base des clichés Daguerre", - "books.google.ca": "Google Books", - "digitalcollections.nypl.org": "NYPL Digital Collections", - "www.bl.uk": "The British Library", - "babel.hathitrust.org": "HathiTrust Digital Library", - "whitmanarchive.org": "The Walt Whitman Archive", - "bivaldi.gva.es": "GVA", - "wellcomeimages.org": "Wellcome Library", - "www.deutsche-digitale-bibliothek.de": "Deutsche Digitale Bibliothek", - "www.nlm.nih.gov": "National Library of Medicine", - "www.kunstkopie.de": "Kunstkopie", - "www.liburuklik.euskadi.eus": "Liburuklik", - "collections.nlm.nih.gov": "National Library of Medicine", - "www.nga.gov": "National Gallery of Art", - "digital.nls.uk": "National Library of Scotland", - "brbl-dl.library.yale.edu": "Yale", - "www.flickr.com": "Flickr", - "blogs.harvard.edu": "Harvard", - "ariyamagga.net": "Ariyamagga", - "sites.nd.edu": "Notre Dame", - "collections.britishart.yale.edu": "Yale", - "en.wikipedia.org": "Wikipedia", - "www.rijksmuseum.nl": "Rijksmuseum", - "www.insidecdcr.ca.gov": "California Department of Corrections and Rehabilitation", - "catalogue.nli.ie": "National Library of Ireland", - "www.christies.com": " Christie's", - "grandmasgraphics.com": "Grandma's Graphics", - "www.oughterardheritage.org": "Oughterard Heritage", - "fineartamerica.com": "Fine Art America", - "50watts.com": "50 Watts", - "art.thewalters.org": "The Walters Art Museum", - "garfield.jtsa.edu:1801": "JTSA", - "www.britishmuseum.org": "British Museum", - "collections.lacma.org": "LACMA", - "www.youtube.com": "YouTube", - "earlyamericanists.com": "The Junto", - "digi.ub.uni-heidelberg.de": "UB Heidelberg", - "www.academia.edu": "Academia.edu", - "www.victorianlondon.org": "The Dictionary of Victorian London", - "blogs.ucl.ac.uk": "UCL", - "sweetgum.nybg.org": "NYBG", - "biodiversitylibrary.org": "Biodiversity Heritage Library", - "dc.lib.unc.edu": "UNC Chapel Hill Libraries", - "4.bp.blogspot.com": "Blogpost", - "www.blumenbach-online.de": "Blumenbach", - "thedabbler.co.uk": "The Dabbler", - "spitalfieldslife.com": "Spitalfields", - "lhldigital.lindahall.org": "LHL Digital", - "parisolympiapress.com": "Paris Olympia Press", - "www.mfa.org": "Boston Museum of Fine Arts", - "bildsuche.digitale-sammlungen.de": "Bildähnlichkeitssuche", - "caliban.mpipz.mpg.de": "Kurt Stüber", - "digital.zbmed.de": "ZB MED", - "www.deutschestextarchiv.de": "Deutsches Textarchiv", - "www.csdl.tamu.edu:8080": "TAMU", - "exhibitions.nypl.org": "The New York Public Library", - "www.qbi2005.com": "QBI Slots", - "www.elfindog.sakura.ne.jp": "Sakura", - "www.getty.edu": "The Getty", - "stuff.mit.edu": "MIT", - "greekmythology.wikia.com": "Greek Mythology Wiki", - "www.wga.hu": "Web Gallery of Art", - "www.lib.unimelb.edu.au": "UNIMELB", - "www.the-athenaeum.org": "The Athenaeum", - "www.dnalc.org": "DNA Learning Center", - "www.digitalcommonwealth.org": "Digital Commonwealth", - "postalmuseum.si.edu": "Postal Museum", - "streetsofsalem.files.wordpress.com": "SOS", - "www.nyu.edu": "NYU", - "www.archive.org": "Internet Archive", - "www.artic.edu": "The Art Institute of Chicago", - "www.unesco-ci.org": "Unesco Ci", - "wellcomelibrary.org": "Wellcome Library", - "luna.folger.edu": "Folger Digital Image Collection", - "florencegriswoldmuseum.org": "Florence Griswold Museum", - "thecanadasite.com": "The Canada Site", - "digital.lib.uh.edu": "University of Houston", - "www.dreweatts.com": "Dreweatts", - "www.gutenberg.org": "Gutenberg", - "hrc.contentdm.oclc.org": "CONTENTdm", - "jv.gilead.org.il": "Zvi Har’El", - "airandspace.si.edu": "National Air and Space Museum |", - "imaginaryinstruments.org": "Museum of Imaginary Musical Instruments", - "jaredandkyal.files.wordpress.com": "jaredandkyal", - "digital.ub.uni-duesseldorf.de": "ULB Düsseldorf", - "ebooks.adelaide.edu.au": "Adelaide", - "images.library.yale.edu": "Yale", - "unruly18thcentury.blogspot.co.uk": "Unruly Eighteenth-Century Britain", - "s-media-cache-ak0.pinimg.com": "PINIMG", - "www.altomagazine.com": "Elite Traveler", - "www.pinterest.com": "Pinterest", - "www.tate.org.uk": "Tate", - "jcb.lunaimaging.com": "JCB", - "www.bne.es": "Biblioteca Nacional", - "circulatingnow.nlm.nih.gov": "NLM", - "books.google.co.uk": "Google Books", - "museumofthemind.org.uk": "Bethlem Museum of the Mind", - "www.stltoday.com": "St. Louis website", - "www.google.com": "Google", - "web.stanford.edu": "Stanford University", - "www.themorgan.org": "The Morgan Library & Museum", - "www.armitt.com": "ARMITT", - "bibliodyssey.blogspot.de": "BibliOdyssey", - "www.vam.ac.uk": "V&A", - "blog.etsy.com": "Etsy", - "www.berfrois.com": "Berfrois", - "publicdomainreview.org": "The Public Domain Review", - "kimberlyevemusings.blogspot.de": "Victorian Musings", - "www.amdigital.co.uk": "Adam Matthew Digital", - "www.nasa.gov": "NASA", - "memory.loc.gov": "American Memory: Remaining Collections", - "hdl.handle.net": "Handle Proxy", - "upload.wikimedia.org": "Wikimedia Commons", - "theibtaurisblog.com": "The I.B.Tauris Blog", - "museejjrousseau.montmorency.fr": "Musée Jean-Jacques Rousseau de Montmorency", - "www.amazon.com": "Amazon", - "www.oldlongisland.com": "Old Long Island", - "www.jssgallery.org": "John Singer Sargent Virtual Gallery", - "www.uni-salzburg.at": "Salzburg Uni", - "www.pirates-corsaires.com": "Pirates & Corsaires", - "farm8.staticflickr.com": "Flickr", - "collections.countway.harvard.edu": "Harvard", - "diglib.hab.de": "Wolfenbütteler Digitale Bibliothek", - "echo.mpiwg-berlin.mpg.de": "ECHO", - "www.americanantiquarian.org": "American Antiquarian Society", - "www.wormfood.com": "Amy Stewart", - "play.google.com": "Google Play", - "pressandpolicy.bl.uk": "The British Library", - "farm9.staticflickr.com": "Flickr", - "www.chemheritage.org": "Science History Institute", - "www.wikipaintings.org": "WikiArt", - "special.lib.gla.ac.uk": "University of Glasgow", - "uploads4.wikipaintings.org": "Wikipaintings", - "de.wikisource.org": "Wikisource", - "www.spamula.net": "Spamula", - "2.bp.blogspot.com": "Blogspot", - "www.bsb-muenchen.de": "BSB Muenchen", - "fullerton.academia.edu": "CSU Fullerton", - "iiif.lib.harvard.edu": "Harvard", - "theappendix.net": "The Appendix" -} diff --git a/app/dashboard/import/sources/pdr/thumbnails.txt b/app/dashboard/import/sources/pdr/thumbnails.txt deleted file mode 100644 index ab19fe67c2e..00000000000 --- a/app/dashboard/import/sources/pdr/thumbnails.txt +++ /dev/null @@ -1,62 +0,0 @@ -2018 - -_39729072460_448fe7f96a_o.jpg -_27339783618_645c8ec010_b.jpg -_39537337401_be82242e19_b.jpg -_39729603801_9139865171_b.jpg -_39419393964_0d43e5b285_b.jpg -_26424139858_4308f81570_b.jpg -_40626273312_8d5eae3625_b.jpg -_40897185072_0ba58deaf2_b.jpg - -2017 - -_32477923166_11ac077bbf_c.jpg -_32739208636_d999567ce1_o.jpg -_32191435154_e1006a7696_b.jpg -_32939152210_bfaa856eec_z.jpg -_33458219511_0deb0817ec_b.jpg -_32914513074_fa5d57cd6f_o.jpg -_33961375642_c80044b627_b.jpg -_34385852236_fc37ca430c_c.jpg -_33871440234_ba0916e1f6_o.jpg -_34947278816_d8a53da104_o.jpg -_35172568521_7fcc9ecb10_c.jpg -_35549576736_8556a26a90_b.jpg -_35835912416_83efcb9699_o.jpg -_36025209781_cefe072bb2_c.jpg -_35772554654_75211b60f6_o.jpg -_36742555122_9a3629b8a7_o.png -_36267618664_27587c1a18_c.jpg -_36665252783_0c28b3aa92_b.jpg -_37532093481_8ec24a70f4_b.jpg -_37197683024_150a11b4b4_c.jpg -_38287703681_737264203c_b.jpg -_37691151385_b01b06bc2b_b.jpg -_27115095529_7abcd5c0ba_b.jpg - -2016 - -_23565530654_c3ae721476_o.jpg -_Christopher_Marlowe.jpg -_24269152474_1e7a6ff2c6_b.jpg -_24866292319_20cfaef5f5_b.jpg -_25314329950_1674c63145_b.jpg -_25964387156_a8a91976a1_b.jpg -_25999339080_14357d6bd8_o.jpg -_25950321504_9e2b83950a_b.jpg -_26793334355_10e53e58db_c.jpg -_26470194873_92e96484c3_o.jpg -_27301604661_7841ff0f1e_h.jpg -_27612383666_cc02e8cf9f_b.jpg -_800px-Erotische_Aufnahme_c1880s.jpg -_28326632622_a7b89c65c0_b.jpg -_28081241544_8b6ce4aef1_b.jpg -_28737899780_6b78015994_c.jpg -_29464111776_7bb40aedda_b.jpg -_29405343460_601ae260fa_c.jpg -_29645543523_bca916974a_c.jpg -_800px-1572_Typus_Orbis_Terrarum_Ortelius.jpg -_30898148625_c6c3bd4aa5_b.jpg -_800px-Illustrations_to_Robert_Blair%27s_The_Grave_%2C_object_9_The_Soul_Hovering_over_the_Body.jpg -_30638906824_0afa3f480d_o.jpg diff --git a/app/dashboard/import/sources/pdr/tidy_figures.js b/app/dashboard/import/sources/pdr/tidy_figures.js deleted file mode 100644 index 99b39c8704d..00000000000 --- a/app/dashboard/import/sources/pdr/tidy_figures.js +++ /dev/null @@ -1,83 +0,0 @@ -var pretty = require("pretty"); -var parse = require("url").parse; -var request = require("request"); -var fs = require("fs-extra"); -var SOURCES = require("./sources.json"); - -function fetch_source(host) { - request("http://" + host, function (err, res, body) { - if (err || !body) { - console.log("HOST", host); - return; - } - - var $ = require("cheerio").load(body); - var title = $("head title").text(); - - if (title) SOURCES[host] = title; - - fs.outputJsonSync(__dirname + "/sources.json", SOURCES, { spaces: 2 }); - }); -} -module.exports = function ($) { - $("figure, figcaption, figure img").each(function (i, el) { - $(el).removeAttr("class"); - }); - - $("figure").each(function (i, el) { - $(el).replaceWith(pretty($.html(el))); - }); - - $("figure img").each(function (i, el) { - $(el).removeAttr("width"); - $(el).removeAttr("height"); - }); - - $("figcaption a").each(function (i, el) { - // console.log('BEFORE:', parent.text().split('\n').join('')); - - if ($(el).text().toLowerCase().indexOf("source") === -1) return; - - var host = $(el).attr("href"); - - var parsed_host = parse(host); - - if (parsed_host.host) host = parsed_host.host; - - // Don't ask - if (parsed_host.protocol === "file:") host = "commons.wikimedia.org"; - - $(el).removeAttr("rel"); - $(el).removeAttr("target"); - - if (el.prev.type === "text") { - var data = el.prev.data; - - if (data.trim().slice(-1) === "—") { - data = data.trim().slice(0, -1); - } - - if (el.next.data.indexOf(".") > -1) data = data.trim() + "."; - - data += " "; - - el.prev.data = data; - } - - if (el.next.type === "text") { - $(el.next).remove(); - } - - if (SOURCES[host]) { - host = SOURCES[host]; - } else { - fetch_source(host); - } - - $(el).text(host); - - $(el).replaceWith($("" + $.html(el) + "")); - - // console.log('AFTER:', parent.text().split('\n').join('')); - }); -}; diff --git a/app/dashboard/import/sources/pdr/tidy_footnotes.js b/app/dashboard/import/sources/pdr/tidy_footnotes.js deleted file mode 100644 index ef4846c2fa0..00000000000 --- a/app/dashboard/import/sources/pdr/tidy_footnotes.js +++ /dev/null @@ -1,26 +0,0 @@ -var parse = require("url").parse; - -module.exports = function ($) { - // Remove hr before footnotes definition - // since the markdown parser creates a new one - $('sup[id="fn1"]').parent().prevUntil("hr").prev().remove(); - - // Fix bare URLs in footnotes - $('sup[id^="fn"]').each(function (i, el) { - $(el) - .find("a") - .each(function (i, el) { - var link_text = $(el).text(); - - if (link_text.indexOf("://") === -1) return; - - try { - link_text = parse(link_text).host; - } catch (e) { - return; - } - - if (link_text) $(el).text(link_text); - }); - }); -}; diff --git a/app/dashboard/import/sources/pdr/tidy_sources.js b/app/dashboard/import/sources/pdr/tidy_sources.js deleted file mode 100644 index c49bc1eeadf..00000000000 --- a/app/dashboard/import/sources/pdr/tidy_sources.js +++ /dev/null @@ -1,15 +0,0 @@ -var sources = require("./sources"); -var fs = require("fs-extra"); - -for (var i in sources) { - var label = sources[i]; - - label = label.split("\n").join(""); - label = label.split("\r").join(""); - label = label.split("\t").join(""); - - sources[i] = label; -} - -fs.outputJsonSync("sources-fixed.json", sources, { spaces: 2 }); -console.log("Done!"); diff --git a/app/dashboard/import/sources/tumblr/load.js b/app/dashboard/import/sources/tumblr/load.js index 236a05738eb..922480d2524 100644 --- a/app/dashboard/import/sources/tumblr/load.js +++ b/app/dashboard/import/sources/tumblr/load.js @@ -1,114 +1,76 @@ -var fs = require("fs-extra"); -var string = require("string"); -var API_KEY = process.env.BLOT_TUMBLR_KEY; -var URL_TEMPLATE = +const fetch = require("node-fetch"); +const fs = require("fs-extra"); +const API_KEY = process.env.BLOT_TUMBLR_KEY; +const URL_TEMPLATE = "http://api.tumblr.com/v2/blog/{{url}}/{{resource}}?api_key={{API_KEY}}"; -var request = require("request"); -var helper = require("dashboard/importer/helper"); -var for_each = helper.for_each; -var parse = require("url").parse; -function tidy_source_url(source_url) { - source_url = source_url.split("http://").join(""); - source_url = source_url.split("https://").join(""); - source_url = source_url.split("/").join(""); - - return source_url; +function tidy_source_url (source_url) { + return source_url + .replace(/https?:\/\//, "") + .split("/") + .join(""); } -function main(source_url, callback) { - var blog = {}; - +async function main (source_url) { source_url = tidy_source_url(source_url); - get_info(source_url, function (err, info) { - if (err) return callback(err); + const info = await get_info(source_url); + const posts = await get_posts(source_url, info.total_posts); - get_posts(source_url, info.total_posts, function (err, posts) { - if (err) return callback(err); + const blog = { + title: info.title, + posts: posts, + host: new URL(info.url).host + }; - blog.title = info.title; - blog.posts = posts; - blog.host = parse(info.url).host; - - return callback(null, blog); - }); - }); + return blog; } -function get_posts(source_url, total_posts, callback) { - var urls = []; - var posts = []; - - for (var offset = 0; offset < total_posts; offset += 20) { - urls.push( - string(URL_TEMPLATE + "&offset={{offset}}&limit={{limit}}").template({ - url: source_url, - offset: offset, - limit: 20, - API_KEY: API_KEY, - resource: "posts", - }).s - ); +async function get_posts (source_url, total_posts) { + let posts = []; + const limit = 20; + + for (let offset = 0; offset < total_posts; offset += limit) { + const url = URL_TEMPLATE.replace("{{url}}", source_url) + .replace("{{offset}}", offset) + .replace("{{limit}}", limit) + .replace("{{API_KEY}}", API_KEY) + .replace("{{resource}}", "posts"); + console.log(posts.length); + + const response = await fetch(url); + const body = await response.json(); + posts = posts.concat(body.response.posts); } - for_each( - urls, - function (url, next) { - console.log(posts.length); - - request(url, function (err, res, body) { - if (err) return callback(err); - - body = JSON.parse(body); - body = body.response; - - posts = posts.concat(body.posts); - - next(); - }); - }, - function () { - console.log("Done!"); - callback(null, posts); - } - ); + console.log("Done!"); + return posts; } -function get_info(source_url, callback) { - var url = string(URL_TEMPLATE).template({ - url: source_url, - API_KEY: API_KEY, - resource: "info", - }).s; - - request(url, function (err, res, body) { - if (err) return callback(err); +async function get_info (source_url) { + const url = URL_TEMPLATE.replace("{{url}}", source_url) + .replace("{{API_KEY}}", API_KEY) + .replace("{{resource}}", "info"); - body = JSON.parse(body); - body = body.response.blog; - - return callback(null, body); - }); + const response = await fetch(url); + const body = await response.json(); + return body.response.blog; } if (require.main === module) { - var source_url = process.argv[2]; - var output_file = process.argv[3]; + const source_url = process.argv[2]; + const output_file = process.argv[3]; if (!source_url) throw new Error("Please pass tumblr URL as first argument"); if (!output_file) throw new Error("Please pass filename to write blog to as second argument"); - main(source_url, function (err, blog) { - if (err) throw err; - - fs.outputJson(output_file, blog, { spaces: 2 }, function (err) { - if (err) throw err; - - process.exit(); + main(source_url) + .then(blog => fs.outputJson(output_file, blog, { spaces: 2 })) + .then(() => process.exit()) + .catch(err => { + throw err; }); - }); } module.exports = main; diff --git a/app/dashboard/settings/verify-domain.js b/app/dashboard/settings/verify-domain.js index 181574adfea..2d059096c5c 100644 --- a/app/dashboard/settings/verify-domain.js +++ b/app/dashboard/settings/verify-domain.js @@ -1,30 +1,31 @@ -var makeRequest = require("request"); -var url = require("url"); +const fetch = require("node-fetch"); +const url = require("url"); -// Called on the site to call the individual -// blog. We could make this call directly but -// to do so would violate our CSP. This is posssibly safer module.exports = function (request, response) { - var domain = request.params.domain; - domain = domain.replace(" ", ""); + let domain = request.params.domain; + domain = domain.replace(/\s/g, ""); // Remove all spaces - if (domain.indexOf("//") > -1) domain = url.parse(domain).hostname; + if (domain.indexOf("//") > -1) domain = new URL(domain).hostname; - var options = { - // Change this to https is the - // user requries SSL to visit blog - uri: "http://" + domain + "/verify/domain-setup", + // Use HTTPS to ensure secure communication + const endpoint = `https://${domain}/verify/domain-setup`; - // The request module has a known bug - // which leaks memory and event emitters - // during redirects. We cap the maximum - // redirects to 5 to avoid encountering - // errors when it creates 10+ emitters - // for a URL with 10+ redirects... - maxRedirects: 5, - }; - - makeRequest(options, function (error, res, body) { - response.send(body === request.blog.handle); - }); + fetch(endpoint) + .then(res => { + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`); + } + return res.text(); + }) + .then(body => { + // Send back a boolean response whether the body matches `request.blog.handle` + response.send(body === request.blog.handle); + }) + .catch(error => { + // Handle any errors that occurred during fetch + console.error("Fetch error:", error); + response + .status(500) + .send("An error occurred while verifying the domain setup"); + }); }; diff --git a/app/helper/transformer/download/index.js b/app/helper/transformer/download/index.js index 0ddfa83c9bc..7d8819b4a87 100644 --- a/app/helper/transformer/download/index.js +++ b/app/helper/transformer/download/index.js @@ -1,24 +1,23 @@ -var ensure = require("helper/ensure"); - -var request = require("request"); -var fs = require("fs"); -var writeStream = fs.createWriteStream; -var UID = require("helper/makeUid"); -var callOnce = require("helper/callOnce"); -var tempDir = require("helper/tempDir")(); -var nameFrom = require("helper/nameFrom"); -var tidy = require("./tidy"); -var invalid = require("./invalid"); - -var IF_NONE_MATCH = "If-None-Match"; -var IF_MODIFIED_SINCE = "If-Modified-Since"; -var LAST_MODIFIED = "last-modified"; -var CACHE_CONTROL = "cache-control"; - -var MAX_REDIRECTS = 5; // prevent event emitter leak... -var TIMEOUT = 5000; // 5s - -var debug = function () {}; // console.log || +const fetch = require("node-fetch"); +const fs = require("fs").promises; +const { createWriteStream } = require("fs"); +const ensure = require("helper/ensure"); +const UID = require("helper/makeUid"); +const callOnce = require("helper/callOnce"); +const tempDir = require("helper/tempDir")(); +const nameFrom = require("helper/nameFrom"); +const tidy = require("./tidy"); +const invalid = require("./invalid"); + +const IF_NONE_MATCH = "If-None-Match"; +const IF_MODIFIED_SINCE = "If-Modified-Since"; +const LAST_MODIFIED = "last-modified"; +const CACHE_CONTROL = "cache-control"; + +const MAX_REDIRECTS = 5; +const TIMEOUT = 5000; // 5s + +const debug = function () {}; // console.log || noop for debugging module.exports = function (url, headers, callback) { // Verify the url has a host, and protocol @@ -33,55 +32,47 @@ module.exports = function (url, headers, callback) { // We don't need to download anything. if (isFresh(headers)) return callback(); - var file, download, path, options; - callback = callOnce(callback); - path = tempDir + UID(6) + "-" + nameFrom(url); - - options = { - headers: { "user-agent": "node-request" }, - maxRedirects: MAX_REDIRECTS, - timeout: TIMEOUT, - url: url, + const path = tempDir + UID(6) + "-" + nameFrom(url); + const file = createWriteStream(path); + + const options = { + headers: { + "User-Agent": "node-fetch", + ...(headers.etag && { [IF_NONE_MATCH]: headers.etag }), + ...(headers[LAST_MODIFIED] && { + [IF_MODIFIED_SINCE]: headers[LAST_MODIFIED] + }) + }, + redirect: "follow", + follow: MAX_REDIRECTS, + timeout: TIMEOUT }; - if (headers && headers.etag) options.headers[IF_NONE_MATCH] = headers.etag; - - if (headers && headers[LAST_MODIFIED]) - options.headers[IF_MODIFIED_SINCE] = headers[LAST_MODIFIED]; - - debug("Downloading", url, "to", path, "with request headers:"); + debug("Downloading", url, "to", path, "with fetch headers:"); debug(print(options.headers)); - file = writeStream(path); - - file.on("error", done).on("finish", file.close); + fetch(url, options) + .then(res => { + debug("Received response:"); - download = request - .get(options) - .on("response", onResponse) - .on("error", done) - .on("end", done); + if (!res.ok) { + debug(" it has a bad status code:", res.status); + throw new Error(res.status); + } - download.pipe(file); + if (res.status === 304) { + debug(" it has 304 unchanged status"); + file.end(); // close the file stream as we won't write anything to it + throw new Error("Not Modified"); + } - function onResponse(res) { - debug("Recieved response:"); - - if (!res || !res.statusCode) { - debug(" it is empty or without status code"); - return callback(new Error("No response")); - } - - if (res.headers) { - var cacheControl = res.headers[CACHE_CONTROL]; - var lastModified = res.headers[LAST_MODIFIED]; - var expires = res.headers.expires; - - // etags sometimes have " inside a string - // dont remove these or it wont work... - var etag = res.headers.etag; + // Update response headers + const cacheControl = res.headers.get(CACHE_CONTROL); + const lastModified = res.headers.get(LAST_MODIFIED); + const expires = res.headers.get("expires"); + const etag = res.headers.get("etag"); headers[LAST_MODIFIED] = lastModified || headers[LAST_MODIFIED] || ""; headers.etag = etag || headers.etag || ""; @@ -91,63 +82,38 @@ module.exports = function (url, headers, callback) { headers.expires || ""; - debug(" updated latest reponse headers for status", res.statusCode); - } - - if (res.statusCode === 304) { - debug(" it has 304 unchanged status"); - stop(); - return done(); - } - - // can we make this play nicely with redirects? - if (res.statusCode < 200 || res.statusCode >= 300) { - debug(" it has a bad status code:", res.statusCode); - return done(new Error(res.statusCode)); - } - } - - function stop() { - debug("Aborting download and removing", path); - - download.abort(); - - // we reset the path since the - // download was stopped, there is no local file. - // do nothing don't care if this errors - try { - fs.unlink(path, function (err) { - if (err) debug(err); - }); - } catch (e) {} - - path = null; - } - - function done(err) { - if (err) stop(); + debug(" updated latest response headers for status", res.status); + res.body.pipe(file); // start piping the response body to the file - debug("Calling back with path", path, "and res headers:"); - debug(print(headers)); - - return callback(err, path, headers); - } + return new Promise((resolve, reject) => { + file.on("finish", resolve); + file.on("error", reject); + }); + }) + .then(() => { + debug("Calling back with path", path, "and res headers:"); + debug(print(headers)); + callback(null, path, headers); + }) + .catch(err => { + debug("Download error:", err); + file.close(); + fs.unlink(path).catch(() => {}); + callback(err); + }); }; -function isFresh(existing) { +function isFresh (existing) { return ( existing && existing.url && existing.expires && - existing.expires > Date.now() + new Date(existing.expires) > new Date() ); } -function print(obj) { - var res = ""; - for (var i in obj) { - if (res) res += "\n"; - res += " " + i + ': "' + obj[i] + '"'; - } - return res; +function print (obj) { + return Object.entries(obj) + .map(([key, value]) => ` ${key}: "${value}"`) + .join("\n"); } diff --git a/app/tests/blog/drafts.js b/app/tests/blog/drafts.js index 63349cccd7b..8fc5cd09f35 100644 --- a/app/tests/blog/drafts.js +++ b/app/tests/blog/drafts.js @@ -4,7 +4,7 @@ describe("drafts work", function () { const sync = require("sync"); const blogServer = require("../../blog"); const fs = require("fs-extra"); - const request = require("request"); + const fetch = require("node-fetch"); const Express = require("express"); const config = require("config"); const guid = require("helper/guid"); @@ -16,21 +16,39 @@ describe("drafts work", function () { const firstContents = guid(); const secondContents = guid(); - this.writeDraft(path, firstContents, (err) => { + this.writeDraft(path, firstContents, err => { if (err) return done.fail(err); - request(this.origin + "/draft/stream" + path, { strictSSL: false }) - .on("response", () => { - this.writeDraft(path, secondContents, (err) => { - if (err) return done.fail(err); + const { Readable } = require("stream"); + + fetch(this.origin + "/draft/stream" + path) + .then(res => { + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`); + } + return new Promise((resolve, reject) => { + this.writeDraft(path, secondContents, err => { + if (err) { + reject(err); + } else { + resolve(res.body); // assuming writeDraft processes successfully, we pass on the response body stream + } + }); + }); + }) + .then(body => { + // The response body is a stream. Create a readable stream to consume it. + const readableStream = new Readable().wrap(body); + readableStream.on("data", chunk => { + const data = chunk.toString().trim(); + if (!data) return; + expect(data).toContain(secondContents); + console.log("calling done... HERE!"); + done(); }); }) - .on("data", (data) => { - data = data.toString().trim(); - if (!data) return; - expect(data).toContain(secondContents); - console.log("calling done... HERE!"); - done(); + .catch(err => { + done.fail(err); }); }); }); @@ -40,16 +58,16 @@ describe("drafts work", function () { const view = { name: "entry.html", - content: `{{{entry.html}}}`, + content: `{{{entry.html}}}` }; - Template.create(this.blog.id, templateName, {}, (err) => { + Template.create(this.blog.id, templateName, {}, err => { if (err) return done(err); Template.getTemplateList(this.blog.id, (err, templates) => { let templateId = templates.filter( ({ name }) => name === templateName )[0].id; - Template.setView(templateId, view, (err) => { + Template.setView(templateId, view, err => { if (err) return done(err); Blog.set( this.blog.id, @@ -67,7 +85,7 @@ describe("drafts work", function () { server = Express(); server.use((req, res, next) => { const _get = req.get; - req.get = (arg) => { + req.get = arg => { if (arg === "host") { return `${this.blog.handle}.${config.host}`; } else return _get(arg); @@ -75,7 +93,7 @@ describe("drafts work", function () { next(); }); server.use(blogServer); - this.origin = "http://localhost:" + 8919; + this.origin = "http://127.0.0.1:" + 8919; this.server = server.listen(8919, done); this.writeDraft = (path, contents, callback) => { diff --git a/app/tests/blog/scheduled-post.js b/app/tests/blog/scheduled-post.js index 6885829d699..93d336854e2 100644 --- a/app/tests/blog/scheduled-post.js +++ b/app/tests/blog/scheduled-post.js @@ -6,7 +6,6 @@ xdescribe("scheduled posts", function () { const sync = require("sync"); const blogServer = require("../../blog"); const fs = require("fs-extra"); - const request = require("request"); const Express = require("express"); const config = require("config"); const guid = require("helper/guid"); @@ -33,7 +32,7 @@ xdescribe("scheduled posts", function () { }); }, MINUTE); fs.outputFileSync(this.blogDirectory + path, contents, "utf-8"); - folder.update(path, (err) => { + folder.update(path, err => { if (err) return callback(err); finish(null, () => { request(this.origin, { strictSSL: false }, (err, res, body) => { @@ -51,16 +50,16 @@ xdescribe("scheduled posts", function () { const view = { name: "entries.html", - content: `{{#entries}}{{{html}}}{{/entries}}`, + content: `{{#entries}}{{{html}}}{{/entries}}` }; - Template.create(this.blog.id, templateName, {}, (err) => { + Template.create(this.blog.id, templateName, {}, err => { if (err) return done(err); Template.getTemplateList(this.blog.id, (err, templates) => { let templateId = templates.filter( ({ name }) => name === templateName )[0].id; - Template.setView(templateId, view, (err) => { + Template.setView(templateId, view, err => { if (err) return done(err); Blog.set( this.blog.id, @@ -77,7 +76,7 @@ xdescribe("scheduled posts", function () { this.server = Express() .use((req, res, next) => { const _get = req.get; - req.get = (arg) => { + req.get = arg => { if (arg === "host") { return `${this.blog.handle}.${config.host}`; } else return _get(arg); diff --git a/app/tests/dates/index.js b/app/tests/dates/index.js index f43e6abdff1..17d847ecad4 100644 --- a/app/tests/dates/index.js +++ b/app/tests/dates/index.js @@ -4,7 +4,7 @@ describe("date integration tests", function () { const fs = require("fs-extra"); const Blog = require("models/blog"); const Template = require("models/template"); - const request = require("request"); + const fetch = require("node-fetch"); const Express = require("express"); const config = require("config"); @@ -17,18 +17,18 @@ describe("date integration tests", function () { { timeZone: "UTC", dateMetadata: "1/2/2012", - result: "Mon, 02 Jan 2012 00:00:00 +0000", + result: "Mon, 02 Jan 2012 00:00:00 +0000" }, { timeZone: "Asia/Calcutta", dateMetadata: "2020-03-29T19:29:00+0530", - result: "Sun, 29 Mar 2020 19:29:00 +0530", + result: "Sun, 29 Mar 2020 19:29:00 +0530" }, { timeZone: "Asia/Calcutta", dateMetadata: "2020/03/29 19:29", - result: "Sun, 29 Mar 2020 19:29:00 +0530", - }, + result: "Sun, 29 Mar 2020 19:29:00 +0530" + } ]; tests.forEach(({ timeZone, dateMetadata, result }) => { @@ -48,13 +48,13 @@ describe("date integration tests", function () { }); }); - function createTemplate(done) { + function createTemplate (done) { const test = this; const templateName = "example"; const view = { name: "entries.html", - content: `{{#allEntries}}{{#formatDate}}${resultFormat}{{/formatDate}}{{/allEntries}}`, + content: `{{#allEntries}}{{#formatDate}}${resultFormat}{{/formatDate}}{{/allEntries}}` }; Template.create(test.blog.id, templateName, {}, function (err) { @@ -78,7 +78,7 @@ describe("date integration tests", function () { }); } - function createEntryWithDate(test, date, callback) { + function createEntryWithDate (test, date, callback) { const path = "/test.txt"; const contents = `Date: ${date}\n\n# Hello, world\n\nThis is a post.`; sync(test.blog.id, function (err, folder, done) { @@ -91,11 +91,11 @@ describe("date integration tests", function () { }); } - function checkDateOnBlog(test, callback) { - request(test.origin, function (err, res, body) { - expect(res.statusCode).toEqual(200); - callback(null, body.trim()); - }); + async function checkDateOnBlog (test, callback) { + const res = await fetch(test.origin); + expect(res.status).toEqual(200); + const body = await res.text(); + callback(null, body.trim()); } // Create a webserver for testing remote files diff --git a/app/tests/index.js b/app/tests/index.js index cf1031b1b81..91aab498b01 100644 --- a/app/tests/index.js +++ b/app/tests/index.js @@ -11,14 +11,14 @@ describe("Blot configuration", function () { }).not.toThrow(); }); - var request = require("request"); + var fetch = require("node-fetch"); var START_MESSAGE = "listening"; var server; var stderr = ""; beforeAll(function (done) { server = require("child_process").fork(__dirname + "/../../app", { - silent: true, + silent: true }); // App should not emit anything on standard error @@ -37,7 +37,6 @@ describe("Blot configuration", function () { }, LONG_TIMEOUT); afterAll(function (done) { - server.on("exit", function () { done(); }); @@ -45,16 +44,18 @@ describe("Blot configuration", function () { server.kill(); }); - // Stripe and Dropbox webhooks require HTTPS set up, or NGINX - // working, so they are harder to test but it would be nice to - // do this eventually. - it("returns OK at the health endpoint", function (done) { - request("http://localhost:8080/health", function (err, res, body) { - if (err) return done.fail(err); - expect(res.statusCode).toBe(200); - expect(body).toEqual("OK"); - done(); - }); + it("returns OK at the health endpoint", async function () { + console.log("fetching health"); + + const res = await fetch("http://localhost:8080/health"); + + expect(res.status).toBe(200); + + console.log("fetching health body"); + + const body = await res.text(); + + expect(body).toEqual("OK"); }); it("can connect to redis", function (done) { diff --git a/package-lock.json b/package-lock.json index 7068e14a948..e5b9787234f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,7 +63,6 @@ "pushover": "^1.3.6", "rate-limit-redis": "^2.0.0", "redis": "^3.1.2", - "request": "^2.72.0", "sharp": "^0.32.6", "simple-git": "^1.102.0", "stripe": "^2.9.0", @@ -1029,6 +1028,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1343,6 +1343,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, "engines": { "node": ">=0.8" } @@ -1471,7 +1472,8 @@ "node_modules/aws4": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true }, "node_modules/b4a": { "version": "1.6.4", @@ -4249,6 +4251,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true, "engines": [ "node >=0.6.0" ] @@ -4276,7 +4279,8 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, "node_modules/fast-fifo": { "version": "1.3.2", @@ -4302,7 +4306,8 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -6322,6 +6327,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true, "engines": { "node": ">=4" } @@ -6331,6 +6337,7 @@ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "deprecated": "this library is no longer supported", + "dev": true, "dependencies": { "ajv": "^6.12.3", "har-schema": "^2.0.0" @@ -7761,12 +7768,14 @@ "node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -7777,6 +7786,7 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -9033,6 +9043,7 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, "engines": { "node": "*" } @@ -10018,7 +10029,8 @@ "node_modules/psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true }, "node_modules/pstree.remy": { "version": "1.1.8", @@ -10535,6 +10547,7 @@ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dev": true, "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -10565,6 +10578,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true, "engines": { "node": "*" } @@ -10572,12 +10586,14 @@ "node_modules/request/node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true }, "node_modules/request/node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true, "engines": { "node": "*" } @@ -10586,6 +10602,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -10600,6 +10617,7 @@ "version": "1.14.2", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", + "dev": true, "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -10625,12 +10643,14 @@ "node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true }, "node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, "optional": true, "dependencies": { "tweetnacl": "^0.14.3" @@ -10640,6 +10660,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, "dependencies": { "assert-plus": "^1.0.0" }, @@ -10651,6 +10672,7 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, "optional": true, "dependencies": { "jsbn": "~0.1.0" @@ -10660,6 +10682,7 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, "dependencies": { "assert-plus": "^1.0.0" } @@ -10668,38 +10691,45 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, "optional": true }, "node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, "optional": true }, "node_modules/request/node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true }, "node_modules/request/node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true }, "node_modules/request/node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true }, "node_modules/request/node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true }, "node_modules/request/node_modules/qs": { "version": "6.5.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "dev": true, "engines": { "node": ">=0.6" } @@ -10709,6 +10739,7 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", "integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE=", "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, "bin": { "uuid": "bin/uuid" } @@ -12082,6 +12113,7 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, "dependencies": { "psl": "^1.1.28", "punycode": "^2.1.1" @@ -12094,6 +12126,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, "engines": { "node": ">=6" } @@ -12485,6 +12518,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -12493,6 +12527,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, "engines": { "node": ">=6" } @@ -12586,6 +12621,7 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, "engines": [ "node >=0.6.0" ], @@ -13580,6 +13616,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -13822,7 +13859,8 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true }, "ast-transform": { "version": "0.0.0", @@ -13910,7 +13948,8 @@ "aws4": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true }, "b4a": { "version": "1.6.4", @@ -16129,7 +16168,8 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true }, "faker": { "version": "4.1.0", @@ -16151,7 +16191,8 @@ "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, "fast-fifo": { "version": "1.3.2", @@ -16174,7 +16215,8 @@ "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "fast-levenshtein": { "version": "2.0.6", @@ -17805,12 +17847,14 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true }, "har-validator": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, "requires": { "ajv": "^6.12.3", "har-schema": "^2.0.0" @@ -18846,12 +18890,14 @@ "json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -18862,6 +18908,7 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -19879,7 +19926,8 @@ "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true }, "obj-extend": { "version": "0.1.0", @@ -20624,7 +20672,8 @@ "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true }, "pstree.remy": { "version": "1.1.8", @@ -21014,6 +21063,7 @@ "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -21040,22 +21090,26 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -21066,6 +21120,7 @@ "version": "1.14.2", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", + "dev": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -21081,12 +21136,14 @@ "asn1": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, "optional": true, "requires": { "tweetnacl": "^0.14.3" @@ -21096,6 +21153,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -21104,6 +21162,7 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, "optional": true, "requires": { "jsbn": "~0.1.0" @@ -21113,6 +21172,7 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -21121,12 +21181,14 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, "optional": true }, "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, "optional": true } } @@ -21136,32 +21198,38 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true }, "qs": { "version": "6.5.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "dev": true }, "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE=" + "integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE=", + "dev": true } } }, @@ -22242,6 +22310,7 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, "requires": { "psl": "^1.1.28", "punycode": "^2.1.1" @@ -22250,7 +22319,8 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true } } }, @@ -22585,6 +22655,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "requires": { "punycode": "^2.1.0" }, @@ -22592,7 +22663,8 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true } } }, @@ -22665,6 +22737,7 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", diff --git a/package.json b/package.json index 3a177b6923e..a2fef2988e6 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,6 @@ "pushover": "^1.3.6", "rate-limit-redis": "^2.0.0", "redis": "^3.1.2", - "request": "^2.72.0", "sharp": "^0.32.6", "simple-git": "^1.102.0", "stripe": "^2.9.0", diff --git a/tests/broken-links/index.js b/tests/broken-links/index.js index 12e445c991f..562448c4035 100644 --- a/tests/broken-links/index.js +++ b/tests/broken-links/index.js @@ -53,7 +53,6 @@ xdescribe("Blot's website'", function () { it( "does not have any broken links for logged-in users", function (done) { - var request = require("request"); var test = this; request.post(