Change page.jst to use /database.logjson instead of /_analytics/*.json files
authorNick Downing <nick@ndcode.org>
Fri, 7 Jan 2022 06:39:57 +0000 (17:39 +1100)
committerNick Downing <nick@ndcode.org>
Fri, 7 Jan 2022 06:40:19 +0000 (17:40 +1100)
.gitignore
_config/site.jst
link.sh
package.json
page.jst

index 0e2151c..dff39c0 100644 (file)
@@ -4,8 +4,8 @@
 .*.less
 .*.min
 .*.svg
-/_analytics
 /_logs
 /_zet/site.*
+/database.logjson
 /node_modules
 /package-lock.json
index d11c5c1..0298224 100644 (file)
+let assert = require('assert')
 let EmailJSCache = require('@ndcode/emailjs_cache')
 let ZettairCache = require('@ndcode/zettair_cache')
-let assert = require('assert')
 
-let CustomSite = function(resources, root, options, prev_site) {
-  if (!this instanceof CustomSite)
-    throw Error('CustomSite is a constructor')
-  _jst_server.Site.call(this, resources, root, options, prev_site)
+return async (resources, root, prev_site) => {
+  let logjson = (await import('@ndcode/logjson')).default
 
-  this.emailjs_cache = undefined
-  this.zettair_cache = undefined
-}
+  let CustomSite = function(resources, root, options, prev_site) {
+    if (!this instanceof CustomSite)
+      throw Error('CustomSite is a constructor')
+    _jst_server.Site.call(this, resources, root, options, prev_site)
 
-CustomSite.prototype = Object.create(_jst_server.Site.prototype)
+    this.database = undefined
+    this.emailjs_cache = undefined
+    this.zettair_cache = undefined
+  }
 
-CustomSite.prototype.start = async function() {
-  await _jst_server.Site.prototype.start.call(this)
+  CustomSite.prototype = Object.create(_jst_server.Site.prototype)
+
+  // called when the server starts or the site.jst file is modified
+  // in latter case it will carry over the previously created resource objects
+  CustomSite.prototype.start = async function() {
+    await _jst_server.Site.prototype.start.call(this)
+
+    assert(this.database === undefined)
+    this.database = await this.resources.ref(
+      'database',
+      async () => {
+        let database = new logjson.Database()
+        await database.open(this.root + '/database.logjson')
+        return database
+      }
+    )
+
+    assert(this.emailjs_cache === undefined)
+    this.emailjs_cache = await this.resources.ref(
+      'emailjs_cache',
+      async () => new EmailJSCache(true)
+    )
+
+    assert(this.zettair_cache === undefined)
+    this.zettair_cache = await this.resources.ref(
+      'zettair_cache',
+      async () => new ZettairCache(true)
+    )
+  }
 
-  assert(this.emailjs_cache === undefined)
-  this.emailjs_cache = await this.resources.ref(
-    'emailjs_cache',
-    async () => new EmailJSCache(true)
-  )
+  // called when the server starts or the site.jst file is modified
+  // in latter case the start() method of the new CustomSite object is called
+  // first and then the stop() method of the old CustomSite object, so that the
+  // reference counting can keep the resource objects alive during changeover
+  CustomSite.prototype.stop = async function() {
+    await _jst_server.Site.prototype.stop.call(this)
 
-  assert(this.zettair_cache === undefined)
-  this.zettair_cache = await this.resources.ref(
-    'zettair_cache',
-    async () => new ZettairCache(true)
-  )
-}
+    assert(this.database !== undefined)
+    await this.resources.unref('database')
 
-CustomSite.prototype.stop = async function() {
-  await _jst_server.Site.prototype.stop.call(this)
+    assert(this.emailjs_cache !== undefined)
+    await this.resources.unref('emailjs_cache')
 
-  assert(this.emailjs_cache !== undefined)
-  await this.resources.unref('emailjs_cache')
+    assert(this.zettair_cache !== undefined)
+    await this.resources.unref('zettair_cache')
+  }
 
-  assert(this.zettair_cache !== undefined)
-  await this.resources.unref('zettair_cache')
-}
+  // called once per second, responsible for cache cleaning and flushing
+  CustomSite.prototype.kick = async function() {
+    await _jst_server.Site.prototype.kick.call(this)
 
-CustomSite.prototype.kick = async function() {
-  await _jst_server.Site.prototype.kick.call(this)
+    assert(this.database !== undefined)
+    this.database.kick()
 
-  assert(this.emailjs_cache !== undefined)
-  this.emailjs_cache.kick()
+    assert(this.emailjs_cache !== undefined)
+    this.emailjs_cache.kick()
 
-  assert(this.zettair_cache !== undefined)
-  this.zettair_cache.kick()
-}
+    assert(this.zettair_cache !== undefined)
+    this.zettair_cache.kick()
+  }
 
-CustomSite.prototype.get_emailjs = function(pathname) {
-  return /*await*/ this.emailjs_cache.get(this.root + pathname)
-}
+  // retrieves a particular email account (loaded into an emailjs object)
+  CustomSite.prototype.get_emailjs = function(pathname) {
+    return /*await*/ this.emailjs_cache.get(this.root + pathname)
+  }
 
-CustomSite.prototype.get_zettair = function(pathname) {
-  return /*await*/ this.zettair_cache.get(this.root + pathname)
-}
+  // retrieves a particular search index (node.js wrapper of a zettair object)
+  CustomSite.prototype.get_zettair = function(pathname) {
+    return /*await*/ this.zettair_cache.get(this.root + pathname)
+  }
 
-CustomSite.prototype.respond = async function(env) {
-  if (
-    env.parsed_url.pathname === '/node_modules' ||
-    env.parsed_url.pathname.slice(0, 14) === '/node_modules/' ||
-    env.parsed_url.pathname === '/package.json' ||
-    env.parsed_url.pathname === '/package-lock.json'
-  ) {
-    this.die(env, `banned file ${env.parsed_url.pathname}`)
-    return
+  // customize the file search/serving algorithm for this particular website
+  CustomSite.prototype.respond = async function(env) {
+    if (
+      env.parsed_url.pathname === '/node_modules' ||
+      env.parsed_url.pathname.slice(0, 14) === '/node_modules/' ||
+      env.parsed_url.pathname === '/package.json' ||
+      env.parsed_url.pathname === '/package-lock.json'
+    ) {
+      this.die(env, `banned file ${env.parsed_url.pathname}`)
+      return
+    }
+    return /*await*/ _jst_server.Site.prototype.respond.call(this, env)
   }
-  return /*await*/ _jst_server.Site.prototype.respond.call(this, env)
-}
 
-return async (resources, root, prev_site) => new CustomSite(
-  resources,
-  root,
-  {},
-  prev_site
-)
+  return new CustomSite(
+    resources,
+    root,
+    {},
+    prev_site
+  )
+}
diff --git a/link.sh b/link.sh
index 0894a32..09936fd 100755 (executable)
--- a/link.sh
+++ b/link.sh
@@ -1,5 +1,5 @@
 #!/bin/sh
 rm -rf node_modules package-lock.json
-npm link @ndcode/emailjs_cache @ndcode/zettair_cache
+npm link @ndcode/emailjs_cache @ndcode/logjson @ndcode/zettair_cache
 npm install
 npm link
index 3b52d86..52a4ddc 100644 (file)
@@ -5,6 +5,7 @@
   "directories": {},
   "dependencies": {
     "@ndcode/emailjs_cache": "^0.1.0",
+    "@ndcode/logjson": "^0.1.0",
     "@ndcode/zettair_cache": "^0.1.0",
     "cookie": "^0.3.1",
     "querystring": "^0.2.0",
index caef8d3..106b253 100644 (file)
--- a/page.jst
+++ b/page.jst
@@ -6,28 +6,21 @@ return async (env, head, body, scripts) => {
   let favicons = await env.site.get_min_html('/_favicon/favicons.html')
   let globals = await env.site.get_json('/_config/globals.json')
 
-  await env.site.ensure_dir('/_analytics')
-  let sessions = await env.site.read_json('/_analytics/sessions.json', {})
-  let pageviews = await env.site.read_json('/_analytics/pageviews.json', {})
+  let transaction = env.site.database.Transaction()
+  let root = await transaction.get(transaction.LazyObject())
+  let sessions = await root.get('sessions', transaction.LazyObject())
+  let pageviews = await root.get('pageviews', transaction.LazyObject())
 
   let cookies = cookie.parse(env.request.headers.cookie || '')
-  let session_key
-  if (
-    !Object.prototype.hasOwnProperty.call(cookies, 'session_key') ||
-    !Object.prototype.hasOwnProperty.call(
-      sessions,
-      session_key = cookies.session_key
-    )
-  ) {
-    session_key = crypto.randomBytes(16).toString('hex')
-    console.log('new session', session_key)
-    sessions[session_key] = {pageviews: {}}
-  }
-  let session = sessions[session_key]
+  let session_key =
+    Object.prototype.hasOwnProperty.call(cookies, 'session_key') ?
+      cookies.session_key :
+      crypto.randomBytes(16).toString('hex')
+  let session = await sessions.get(session_key, transaction.LazyObject())
     
   let expires = new XDate()
   expires.addMonths(1)
-  session.expires = expires.toUTCString()
+  session.set('expires', expires.toUTCString())
   env.response.setHeader(
     'Set-Cookie',
     'session_key=' +
@@ -37,35 +30,30 @@ return async (env, head, body, scripts) => {
     '; path=/;'
   )
 
-  if (
-    !Object.prototype.hasOwnProperty.call(
-      pageviews,
-      env.parsed_url.pathname
-    )
-  ) {
-    console.log('new pageview', env.parsed_url.pathname)
-    pageviews[env.parsed_url.pathname] = {visits: 0, unique_visits: 0}
-  }
-  let pageview = pageviews[env.parsed_url.pathname]
-  ++pageview.visits;
+  let pageview = await pageviews.get(
+    env.parsed_url.pathname,
+    transaction.LazyObject()
+  )
+  pageview.set(
+    'visits',
+    (await pageview.get('visits') || 0) + 1
+  )
 
-  //if (!Object.prototype.hasOwnProperty.call(session, 'pageviews'))
-  //  session.pageviews = {}
-  let session_pageviews = session.pageviews
-  if (
-    !Object.prototype.hasOwnProperty.call(
-      session_pageviews,
-      env.parsed_url.pathname
+  let session_pageviews = await session.get(
+    'pageviews',
+    transaction.LazyObject()
+  )
+  let i = await session_pageviews.get(env.parsed_url.pathname)
+  if (i === undefined) {
+    pageview.set(
+      'unique_visits',
+      (await pageview.get('unique_visits') || 0) + 1
     )
-  ) {
-    console.log('new session_pageview', env.parsed_url.pathname)
-    session_pageviews[env.parsed_url.pathname] = 0
-    ++pageview.unique_visits
+    i = 0
   }
-  ++session_pageviews[env.parsed_url.pathname]
+  session_pageviews.set(env.parsed_url.pathname, i + 1)
 
-  env.site.write_json('/_analytics/sessions.json', sessions)
-  env.site.write_json('/_analytics/pageviews.json', pageviews)
+  await transaction.commit()
 
   let _out = []
   _out.push('<!DOCTYPE html>')