1 let assert = require('assert')
2 let EmailJSCache = require('@ndcode/emailjs_cache')
3 let XDate = require('xdate')
4 let ZettairCache = require('@ndcode/zettair_cache')
6 return async (resources, root, prev_site) => {
7 let logjson = (await import('@ndcode/logjson')).default
9 let CustomSite = function(resources, root, options, prev_site) {
10 if (!this instanceof CustomSite)
11 throw Error('CustomSite is a constructor')
12 _jst_server.Site.call(this, resources, root, options, prev_site)
14 this.database = undefined
15 this.database_date = new XDate().toUTCString('yyyyMMdd')
16 this.emailjs_cache = undefined
17 this.zettair_cache = undefined
20 CustomSite.prototype = Object.create(_jst_server.Site.prototype)
22 // called when the server starts or the site.jst file is modified
23 // in latter case it will carry over the previously created resource objects
24 CustomSite.prototype.start = async function() {
25 await _jst_server.Site.prototype.start.call(this)
27 assert(this.database === undefined)
28 this.database = await this.resources.ref(
31 let database = new logjson.Database()
32 await database.open(this.root + '/database.logjson')
37 assert(this.emailjs_cache === undefined)
38 this.emailjs_cache = await this.resources.ref(
40 async () => new EmailJSCache(true)
43 assert(this.zettair_cache === undefined)
44 this.zettair_cache = await this.resources.ref(
46 async () => new ZettairCache(true)
50 // called when the server starts or the site.jst file is modified
51 // in latter case the start() method of the new CustomSite object is called
52 // first and then the stop() method of the old CustomSite object, so that the
53 // reference counting can keep the resource objects alive during changeover
54 CustomSite.prototype.stop = async function() {
55 await _jst_server.Site.prototype.stop.call(this)
57 assert(this.database !== undefined)
58 await this.resources.unref('database')
60 assert(this.emailjs_cache !== undefined)
61 await this.resources.unref('emailjs_cache')
63 assert(this.zettair_cache !== undefined)
64 await this.resources.unref('zettair_cache')
67 // called once per second, responsible for cache cleaning and flushing
68 CustomSite.prototype.kick = async function() {
69 await _jst_server.Site.prototype.kick.call(this)
71 assert(this.database !== undefined)
72 await this.database.kick()
74 let new_database_date = new XDate().toUTCString('yyyyMMdd')
75 if (new_database_date !== this.database_date) {
82 await this.database.rotate('database.logjson.' + this.database_date)
83 this.database_date = new_database_date
86 assert(this.emailjs_cache !== undefined)
87 this.emailjs_cache.kick()
89 assert(this.zettair_cache !== undefined)
90 this.zettair_cache.kick()
93 // retrieves a particular email account (loaded into an emailjs object)
94 CustomSite.prototype.get_emailjs = function(pathname) {
95 return /*await*/ this.emailjs_cache.get(this.root + pathname)
98 // retrieves a particular search index (node.js wrapper of a zettair object)
99 CustomSite.prototype.get_zettair = function(pathname) {
100 return /*await*/ this.zettair_cache.get(this.root + pathname)
103 // customize the file search/serving algorithm for this particular website
104 CustomSite.prototype.respond = async function(env) {
106 env.parsed_url.pathname === '/node_modules' ||
107 env.parsed_url.pathname.slice(0, 14) === '/node_modules/' ||
108 env.parsed_url.pathname === '/package.json' ||
109 env.parsed_url.pathname === '/package-lock.json'
111 this.die(env, `banned file ${env.parsed_url.pathname}`)
114 return /*await*/ _jst_server.Site.prototype.respond.call(this, env)
117 return new CustomSite(