diff --git a/request-all-post-images.js b/request-all-post-images.js new file mode 100644 index 0000000..42df376 --- /dev/null +++ b/request-all-post-images.js @@ -0,0 +1,108 @@ +/** + * Reads all posts and requests all images in the mobiledoc. + * + * Why? + * - This is useful to force ghost to create all image versions for all images especially when the content is being consumed by a static(SSG) frontend and the frontend requires responsive images to exist. + * + * Usage: + * + * node request-all-post-images.js http://localhost:2368 ADMIN_API_KEY - dry run + * node request-all-post-images.js http://localhost:2368 ADMIN_API_KEY true - live run + */ + +if (process.argv.length < 4) { + console.log('not enough arguments, provide an API url and admin key'); + process.exit(1); +} + +const Promise = require('bluebird'); +const GhostAdminAPI = require('@tryghost/admin-api'); +const fetch = require('node-fetch'); + +const url = process.argv[2]; +const key = process.argv[3]; + +(async function main() { + const doRerender = process.argv[4] === 'true'; + + if (doRerender) { + console.log('REAL Run'); + } else { + console.log('Dry Run - nothing will be re-rendered'); + } + + // Give the user time to read... + await Promise.delay(1000); + + const api = new GhostAdminAPI({ + url, + key, + version: 'canary' + }); + + function getImagePaths(mobiledocStr) { + const mobiledoc = JSON.parse(mobiledocStr); + const srcs = mobiledoc.cards.map(card => { + const [type, attributes] = card; + return attributes.src; + }); + return srcs + } + + try { + const allPosts = await api.posts.browse({ fields: 'id,slug,mobiledoc', limit: 'all' }); + + console.log(`${allPosts.length} Posts will be accessed`); + + const allImages = []; + + await Promise.mapSeries(allPosts, async (post) => { + console.log(`Reading post ${post.slug} (${post.id})`); + + const images = getImagePaths(post.mobiledoc); + allImages.push(...images); + + return Promise.delay(50).return(true); + }); + + // remove duplicates + const sanitizedImages = [...new Set(allImages)]; + + // remove 'undefined' from the array + sanitizedImages.splice(sanitizedImages.indexOf(undefined), 1); + + const sizes = [600, 1000, 1600]; + + // image versions sample + // Original http://localhost:2368/content/images/2023/05/image.png + // 600w = http://localhost:2368/content/images/size/w600/2023/05/image.png + // 1000w = http://localhost:2368/content/images/size/w1000/2023/05/image.png + // 1600w = http://localhost:2368/content/images/size/w1600/2023/05/image.png + + // generate 600, 1000, 1600 versions of each original image + const allImageVersions = []; + sanitizedImages.forEach(image => { + sizes.forEach(size => { + const imageVersion = image.replace('/content/images/', `/content/images/size/w${size}/`); + allImageVersions.push(imageVersion); + }) + }); + + console.log(`\nFound ${sanitizedImages.length} images that amount to ${allImageVersions.length} versions\n`); + + if (doRerender) { + // do a fetch request for each image version and do nothing with the response + await Promise.mapSeries(allImageVersions, async (image) => { + console.log(`Fetching ${image}`); + await fetch(image); + return Promise.delay(50).return(true); + }); + + + console.log(`\nRequested ${allImageVersions.length} images\n`); + } + } catch (err) { + console.error('There was an error', require('util').inspect(err, false, null)); + process.exit(1); + } +})();