paradigm-core
Version:
Paradigm Core Modules
355 lines (259 loc) • 8.48 kB
JavaScript
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