+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
+ )
+}
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=' +
'; 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>')