UNPKG

paradigm-core

Version:
355 lines (259 loc) 8.48 kB
const fb = require('fb') const { r, logger, RootModel, Cache: {cache} } = require('structure-core') const Highwinds = require('../../lib/highwinds') const {ChannelModel} = require('../channels') class Purge { constructor(options = {}) { this.organizationId = options.organizationId this.applicationId = options.applicationId this.hostname = options.hostname this.logger = options.logger || logger if(!this.logger.debug) { this.logger.debug = console.log.bind(console) } this.protocol = (options.protocol || 'http') + '://' this.urlsPurged = [] this.highwinds = new Highwinds( this.applicationId, `${this.protocol}${this.hostname}` ) this.daHighwinds = new Highwinds( this.applicationId, 'https://da.f12.global' ) } /** * Recursively invalidate all pages of a an application, update the * lastPurgedAt for all documents of the application * * @public */ async purgeAll() { await this.highwinds.purgeAll() await this.daHighwinds.purgeAll() await this.highwinds.updateLastPurgedAt() } /** * Recursively invalidate all pages of a an application, update the * lastPurgedAt for all documents of the application * * @public * @param {String} url - url to purge * @param {Boolean} recursive - whether to purge recursively, default=false */ async purgeUrl(url, recursive = false) { await this.highwinds.purgeUrl(url, recursive) } /** * Clear the cache middleware for an application * * @public */ async clearCache() { await cache.invalidateApplication(this.applicationId) } initFacebook() { return new Promise( (resolve, reject) => { fb.api('oauth/access_token', { client_id: process.env.FACEBOOK_APP_ID, client_secret: process.env.FACEBOOK_APP_SECRET, grant_type: 'client_credentials' }, (res) => { if(!res || res.error) { this.logger.error(res.error) return reject(res.error) } resolve(res.access_token) }) }) } async purgeForChannel(channelSlug) { this.clearCache() return const highwinds = this.highwinds if(!channelSlug) { throw 'Missing channelSlug' } await cache.invalidateApplication(this.applicationId) // Purge the cache await Promise.all([ highwinds.purgeUrl('/api', true), highwinds.purgeUrl('/feed'), highwinds.purgeUrl('/rss'), highwinds.purgeUrl('/ia-rss'), highwinds.purgeUrl('/flipboard-rss'), highwinds.purgeUrl(channelSlug === 'home' ? `` : `/${channelSlug}`) ]) } purgeForDocument(options = {}) { this.clearCache() return const organizationId = this.organizationId const applicationId = this.applicationId const highwinds = this.highwinds const hostname = this.hostname const id = options.id const {DocumentModel} = require('../documents') // prevent circle dep const docModel = new DocumentModel() const rModel = new RootModel({ table: 'actions' }) return new Promise( async (resolve, reject) => { try { if(!id || !applicationId || !organizationId) { return reject(`Missing Ids`, {id, applicationId, organizationId}) } await cache.invalidateApplication(applicationId) const digitalAssetSids = [] const doc = await docModel.getById(id) if(!doc.permalink) { return reject(`Document missing permalink: ${doc.id}`) } let app = await r .table('applications') .get(applicationId) if(app instanceof Array && app.length > 0) app = app[0] let purgeFacebook = false /* PURGE surg keys */ if(app && app.cdn && app.cdn.services && app.cdn.cacheKeys) { if(app.cdn.purgeFacebook) { purgeFacebook = app.cdn.purgeFacebook } const channelAPI = app.cdn.cacheKeys.channelAPI // Site if(app.cdn.services.site) { const site = app.cdn.services.site const hostname = site.hostname highwinds .purgeByTag(channelAPI, {recursive: true}) .then((resp) => { try { rModel.create({ action: 'DOCUMENT_PURGED', applicationId, cacheIds: [resp.id], documentId: id, hostname }) } catch(e) { this.logger.error('Could not purge channel API by tag', e) } }) } // Purge Media using their sid as a cache key if(app.cdn.services.media) { const site = app.cdn.services.media const hostname = site.hostname if(doc.fields instanceof Array) { for(let i = 0, l = doc.fields.length; i < l; i++) { const field = doc.fields[i] if(field.props && field.props.digitalAsset) { digitalAssetSids.push(field.props.digitalAsset.sid) } } } if(doc.featuredImage) { digitalAssetSids.push(doc.featuredImage.sid) } if(doc.social) { if(doc.social.facebook) { digitalAssetSids.push(doc.social.facebook.sid) } if(doc.social.twitter) { digitalAssetSids.push(doc.social.twitter.sid) } } if(doc.user) { digitalAssetSids.push(doc.user.sid) } } } this.logger.debug('Debugging URLs', { urls: ['/api', '', '/feed', '/rss', '/ia-rss', '/flipboard-rss'] }) const purgePromises = [ highwinds.purgeByManyTags(digitalAssetSids), highwinds.purgeUrl(doc.permalink), highwinds.purgeUrl('/ia' + doc.permalink), highwinds.purgeUrl('/amp' + doc.permalink), highwinds.purgeUrl('/fbia' + doc.permalink), highwinds.purgeUrl('/api', true), highwinds.purgeUrl(''), highwinds.purgeUrl('/feed'), highwinds.purgeUrl('/rss'), highwinds.purgeUrl('/flipboard-rss'), ] const channelModel = new ChannelModel({ organizationId, applicationId, logger: this.logger }) const channels = await channelModel.ofDocument(doc.id) for (const channel of channels) { if (channel.slug === 'home') { continue } purgePromises.push(highwinds.purgeUrl(`/${channel.slug}`)) } Promise .all(purgePromises) .then((resp) => { // Wait 10 seconds before starting IA purge setTimeout(function onIAPurge() { highwinds.purgeUrl('/ia-rss') }, 10000) highwinds.updateLastPurgedAt([doc.id]) try { rModel.create({ action: 'DOCUMENT_PURGED', applicationId, cacheIds: [resp[0].id, resp[1].id], documentId: id, hostname }) } catch(e) { this.logger.error('Could not purge urls', e) } }) if(purgeFacebook) { const fbAccessToken = await this.initFacebook() const url = `${this.protocol}${this.hostname}${doc.permalink}` fb.api('/', 'post', { access_token: fbAccessToken, id: url, scrape: true }, (res) => { if(!res || res.error) { this.logger.error('Error busting Facebook cache', res.error) return } this.logger.debug('Facebook cache bust', {url}) }) } resolve(this.urlsPurged) } catch(e) { this.logger.error('Error busting Highwinds cache', e) reject(e) } }) } async purgeForPage(pageSlug) { this.clearCache() return const highwinds = this.highwinds if(!pageSlug) { throw 'Missing pageSlug' } // Purge the cache await highwinds.purgeUrl(`/api/${process.env.API_VERSION}/pages`, true) await highwinds.purgeUrl(`/${pageSlug}`) } } module.exports = Purge