Add cookies and session tracking
authorNick Downing <downing.nick@gmail.com>
Mon, 8 Oct 2018 01:54:19 +0000 (12:54 +1100)
committerNick Downing <downing.nick@gmail.com>
Mon, 8 Oct 2018 01:54:19 +0000 (12:54 +1100)
.gitignore
ndserver.js
package.json

index d502512..477c4b1 100644 (file)
@@ -1,2 +1,3 @@
+/analytics
 /node_modules
 /package-lock.json
index 762aaa0..7914b5e 100755 (executable)
@@ -3,6 +3,8 @@
 let assert = require('assert')
 let BuildCache = require('build_cache')
 let commander = require('commander')
+let cookie = require('cookie')
+let crypto = require('crypto')
 let fs = require('fs')
 let http = require('http')
 let https = require('https')
@@ -13,12 +15,51 @@ var stream_buffers = require('stream-buffers')
 let yauzl = require('yauzl')
 let util = require('util')
 let url = require('url')
+let XDate = require('xdate')
 let zetjs = require('zetjs')
 
+//let _setTimeout = util.promisify(setTimeout)
 let fs_readFile = util.promisify(fs.readFile)
 let fs_stat = util.promisify(fs.stat)
+let fs_writeFile = util.promisify(fs.writeFile)
 let yauzl_open = util.promisify(yauzl.open)
 
+try {
+  fs.mkdirSync('analytics')
+}
+catch (err) {
+  if (err.code !== 'EEXIST') // should check error type
+    throw err
+}
+let sessions
+{
+  let text 
+  try {
+    text = fs.readFileSync('analytics/sessions.json', {encoding: 'utf-8'})
+  }
+  catch (err) {
+    if (err.code !== 'ENOENT') // should check error type
+      throw err
+    text = '{}'
+  }
+  sessions = JSON.parse(text)
+}
+let sessions_dirty = false;
+let sessions_write = async () => {
+  try {
+    sessions_dirty = false;
+    console.log('writing analytics/sessions.json')
+    await fs_writeFile(
+      'analytics/sessions.json',
+      JSON.stringify(sessions),
+      {encoding: 'utf-8'}
+    )
+  }
+  catch (err) {
+    console.error(err.stack || err.message)
+  }
+}
+
 let build_cache_js = new BuildCache()
 let req_js = async path => {
   let result = await build_cache_js.get(path)
@@ -184,7 +225,10 @@ let serve = (res, status, mime_type, data) => {
   // html files will be direct recipient of links/bookmarks so can't have
   // a long lifetime, other files like css or images are often large files
   // and won't change frequently (but we'll need cache busting eventually)
-  if (commander.enableCaching && mime_type !== mime_types['html'])
+  if (
+    commander.enableCaching &&
+    (!mime_types.hasOwnProperty('html') || mime_type !== mime_types['html'])
+  )
     res.setHeader('Cache-Control', 'max-age=3600')
   res.setHeader('Content-Type', mime_type)
   res.setHeader('Content-Length', data.length)
@@ -284,9 +328,36 @@ let app = async (req, res, protocol) => {
     return
   }
 
+  let cookies = cookie.parse(req.headers.cookie || ''), session
+  if (
+    !cookies.hasOwnProperty('session') ||
+    !sessions.hasOwnProperty(site) ||
+    !sessions[site].hasOwnProperty(session = cookies.session)
+  )
+    session = crypto.randomBytes(16).toString('hex')
+  let expires = new XDate()
+  expires.addMonths(1)
+  expires = expires.toUTCString()
+  res.setHeader(
+    'Set-Cookie',
+    'session=' + session + '; expires=' + expires + '; path=/;'
+  )
+  if (!sessions.hasOwnProperty(site))
+    sessions[site] = {}
+  if (!sessions[site].hasOwnProperty(session))
+    sessions[site][session] = {}
+  sessions[site][session].expires = expires
+  if (!sessions_dirty) {
+    sessions_dirty = true
+    setTimeout(sessions_write, 5000)
+  }
+
   temp = file_name.lastIndexOf('.')
   let file_type = temp === -1 ? '' : file_name.substring(temp + 1)
-  let mime_type = mime_types[file_type] || mime_type_default
+  let mime_type =
+    mime_types.hasOwnProperty(file_type) ?
+    mime_types[file_type] :
+    mime_type_default
 
   let page = dir_name + '/' + file_name, data
   if (dir_name_is_pub) {
@@ -382,7 +453,7 @@ let app = async (req, res, protocol) => {
         return
       }
       catch (err) {
-        if (err.code !== 'ENOENT') // note: err.code might be undefined
+        if (err.code !== 'ENOENT') // should check error type
           throw err
       }
       break
@@ -433,7 +504,7 @@ let app = async (req, res, protocol) => {
 let tryApp = (req, res, protocol) => {
   app(req, res, protocol).catch(
     err =>  {
-      console.log(err.stack || err.message)
+      console.error(err.stack || err.message)
       let body =
         '<html><body><pre>' +
         (err.stack || err.message) +
index c59f332..47b1492 100644 (file)
@@ -7,6 +7,7 @@
   "dependencies": {
     "build_cache": "file:../build_cache.git/build_cache-1.0.0.tgz",
     "commander": "^2.18.0",
+    "cookie": "^0.3.1",
     "fs": "^0.0.1-security",
     "http": "^0.0.0",
     "https": "^1.0.0",
@@ -15,6 +16,7 @@
     "querystring": "^0.2.0",
     "stream-buffers": "^3.0.2",
     "url": "^0.11.0",
+    "xdate": "^0.8.2",
     "yauzl": "^2.10.0",
     "zetjs": "file:../zettair.git/src/zetjs/zetjs-1.0.0.tgz"
   },