Skip to content

Implementation on my site

ComputerElite edited this page Nov 20, 2021 · 3 revisions

This will explain how I implemented ComputerAnalytics on my site:

Requirements

  • an heroku account
  • knowing js and html
  • an GitHub account
  • an Discord account and server with a channel

What was my issue?

I did now want to spend money for a C# server. So I could now host ComputerAnalytics

How did I overcome this issue?

I created a js server and hosted in on heroku. This server then recieves all the analytics data and temporarely stores them. After a bit of time all this data is being sent to a Discord channel where I can then download them and import them into ComputerAnalytics running on my PC.

Implementation

js server

Code: https://github.com/ComputerElite/cors-anywhere/blob/8f28cd52d4f249ce0f22f3a8582a8dc4be295c73/lib/cors-anywhere.js Lines: 434 - 460 and 381 - 394

Explaining the code to collect the analytics:

/// The endpoint for the analytics is "/test"
if (location.host === 'test' && req.method == "OPTIONS") { // An OPTIONS request is sent by the browser to check if it is allowed to post the content
      res.writeHead(200,  { "Access-Control-Allow-Origin": "https://computerelite.github.io", "Access-Control-Allow-Methods": "POST, OPTIONS", "Access-Control-Allow-Credentials": "true", "Access-Control-Allow-Headers": "content-type" } ); // This are the headers which the browser expects. https://computerelite.github.io is the site from which it is sending the data
      res.end("") // Finally end the request
      return; // And return from the method
}

if (location.host === 'test' && req.method == "POST") { // The browser sent the data
      const addr = req.headers["x-forwarded-for"] // This is an heroku header which contains the ip from which the request is coming
      res.writeHead(200, {'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json'}); // Write some stuff into the headers
      let data = ''; // create an empty variable to which the content of the POST request is being written
      req.on('data', chunk => { // On data recieve
        data += chunk; // Add the recieved data to the data variable
      })
      req.on('end', () => { // Once all data has been recieved
        var analytic = JSON.parse(data) // parse the json
        analytic.remote = addr // add the ip adress to the analytic
        analytic.uA = req.headers["user-agent"] // Add the user agent to the analytic
        analytics.push(analytic) // Add the analytic to a list of recieved analytics
        res.end(); // End the request
      })
      return; // return
}

And the code to send the analytics to Discord via an webhook

var xhr = new XMLHttpRequest(); // create a new request
xhr.open("POST", process.env.discordwebhookurl2, true); // Start a PSOT request to the Discord webhook url. The url is stored in the environment variable 'discordwebhookurl2' 
xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=----WebKitFormBoundarytEfuBRtEUOxq6z8L'); // set the content type to multipart/form-data and set '----WebKitFormBoundarytEfuBRtEUOxq6z8L' as seperation betweem the data list. '----WebKitFormBoundarytEfuBRtEUOxq6z8L' can basically be anything you want
xhr.send(`------WebKitFormBoundarytEfuBRtEUOxq6z8L                         // Start a bew data
Content-Disposition: form-data; name="file"; filename="analytics.json"     // Set the type of the data to file and the filename to 'analytics.json'
Content-Type: application/json                                             // Set the file type to json
${JSON.stringify(analytics)}                                               // Stringify the list of collected analytics
------WebKitFormBoundarytEfuBRtEUOxq6z8L                                   // Start a bew data
Content-Disposition: form-data; name="payload_json"                        // set the type to 'payload_json' which is what you would normally send to a webhook as json
{"content":"Analytics","tts":false}                                        // That's the Discord message content
------WebKitFormBoundarytEfuBRtEUOxq6z8L--`); // End the data and Send the message.

client-side script

Code: https://github.com/ComputerElite/ComputerElite.github.io/blob/66b86b82711be0a1638322ad8cfb9ce4e2510820/js/standard.js Lines: 146 - 173

const analyticsVersion = "1.0"
var analytic = {
    analyticsVersion: analyticsVersion,      // Add the analyticsVersion
    fullUri: window.location.href,           // Set the content of the address bar
    sideOpen: Math.floor(Date.now() / 1000), // Time when the site has been opened in UTC seconds
    sideClose: 0,                            // temporary value for later
    referrer: document.referrer              // Set the referrer
} // Initialise the basic analytic


var sent = false // variable to prevent sending analytics multiple times

window.onbeforeunload = SendAnalytics // Register Analytics sending on site close
window.onunload = SendAnalytics       // Register Analytics sending on site close
window.onpopstate = SendAnalytics     // Register Analytics sending on site close

function SendAnalytics() {
    if (sent) return // return if the analytics has alread been sent
    sent = true // Set sent to true
    analytic.sideClose = Math.floor(Date.now() / 1000) // Add the time the site has been closed in UTC seconds
    let headers = {
        type: 'application/json' // Set headers
    };
    let blob = new Blob([JSON.stringify(analytic)], headers); // Create the data to send

    navigator.sendBeacon("https://cors-anywhere-computerelite.herokuapp.com/test", blob) // Sends the request even after the site has been closed to the heroku server: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon
}

Viewing the analytics

First I start my ComputerAnalytics Server on my PC. Then in the Discord channel where I got my webhook set up I download all files I haven't imported yet.

I open http://localhost:502/analytics, press the import button at the top and select the files I downloaded from Discord. And finally I reload the page and see all the data in graphical form.

Clone this wiki locally