dustinrecko
2/17/2019 - 8:15 AM

Deduping Google Analytics Transaction Hits - Cloudflare Worker

Deduping Google Analytics Transaction Hits - Cloudflare Worker

addEventListener('fetch', event => {
  if(event.request.url.indexOf('/collect') > -1)
    event.waitUntil(process(event.request))
  event.respondWith(new Response('',{status:200}))
})

/**
 * CONFIG SECTION
 */
const ANALYTICS_URL = 'https://www.google-analytics.com'
const FB_URL = 'https://<your-database-id>.firebaseio.com/transactions/';
const FB_KEY = '<your-database-secret>';

/**
 * Process the hit, but only for transactions that haven't been seen before
 * param {Request} Original Request
 */
const process = async (request) => {
  const url = new URL(request.url)
  const transactionId = url.searchParams.get('ti')
  const hitType = url.searchParams.get('t')

  let entry = await lookup(transactionId)
  let entryData = await entry.json()

  if(entryData)
    return (hitType != 'item') ? logDuplicateHit(transactionId,++entryData.hits) : Promise.resolve(1)
  else {
    return Promise.all([
      (hitType != 'item') ? logTransaction(transactionId) : Promise.resolve(1),
      analyticsHit(decorateHitPayload(url,request.headers.get('CF-Connecting-IP'),encodeURIComponent(request.headers.get('user-agent'))))
    ])
  }
}

/**
 * Check for transaction in Firebase database
 * @param {string} Transaction ID
 */
const lookup = async (id) => (fetch(FB_URL+id+'.json?auth='+FB_KEY))

/**
 * Log new transactions in Firebase database
 * @param {string} Transaction ID
 */
const logTransaction = async (id) => (fetch(new Request(FB_URL+id+'.json?auth='+FB_KEY, {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    createdAt: new Date().getTime(),
    hits: 1
  })
})))

/**
 * Increase hit count for duplicate transactions in Firebase database
 * @param {string} Transaction ID
 * @param {number} Hit Count
 */
const logDuplicateHit = async (id,count) => (fetch(new Request(FB_URL+id+'.json?auth='+FB_KEY, {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    hits: count
  })
})))

/**
 * Decorate the Analytics hit with client's IP and client's User Agent
 * @param {URL} request url
 * @param {string} User's IP
 * @param {string} User's User Agent
 */
const decorateHitPayload = (url,userIP,userAgent) => (url.pathname+url.search+'&ua='+userAgent+'&uip='+userIP)

/**
 * Forward hit to Google Analytics
 * @param {string} Hit Payload 
 */
const analyticsHit = async (hitPayload) => (fetch(ANALYTICS_URL+hitPayload))