b41a6a45270da5ef41fd3cd828ce8d053305bc9c
[ndcode_site.git] / _config / site.jst
1 let assert = require('assert')
2 let EmailJSCache = require('@ndcode/emailjs_cache')
3 let XDate = require('xdate')
4 let ZettairCache = require('@ndcode/zettair_cache')
5
6 return async (resources, root, prev_site) => {
7   let logjson = (await import('@ndcode/logjson')).default
8
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)
13
14     this.database = undefined
15     this.database_date = new XDate().toUTCString('yyyyMMdd')
16     this.emailjs_cache = undefined
17     this.zettair_cache = undefined
18   }
19
20   CustomSite.prototype = Object.create(_jst_server.Site.prototype)
21
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)
26
27     assert(this.database === undefined)
28     this.database = await this.resources.ref(
29       'database',
30       async () => {
31         let database = new logjson.Database()
32         await database.open(this.root + '/database.logjson')
33         return database
34       }
35     )
36
37     assert(this.emailjs_cache === undefined)
38     this.emailjs_cache = await this.resources.ref(
39       'emailjs_cache',
40       async () => new EmailJSCache(true)
41     )
42
43     assert(this.zettair_cache === undefined)
44     this.zettair_cache = await this.resources.ref(
45       'zettair_cache',
46       async () => new ZettairCache(true)
47     )
48   }
49
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)
56
57     assert(this.database !== undefined)
58     await this.resources.unref('database')
59
60     assert(this.emailjs_cache !== undefined)
61     await this.resources.unref('emailjs_cache')
62
63     assert(this.zettair_cache !== undefined)
64     await this.resources.unref('zettair_cache')
65   }
66
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)
70
71     assert(this.database !== undefined)
72     await this.database.kick()
73
74     let new_database_date = new XDate().toUTCString('yyyyMMdd')
75     if (new_database_date !== this.database_date) {
76       console.log(
77         'rotate database',
78         this.database_date,
79         '->',
80         new_database_date
81       )
82       await this.database.rotate('database.logjson.' + this.database_date)
83       this.database_date = new_database_date
84     }
85
86     assert(this.emailjs_cache !== undefined)
87     this.emailjs_cache.kick()
88
89     assert(this.zettair_cache !== undefined)
90     this.zettair_cache.kick()
91   }
92
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)
96   }
97
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)
101   }
102
103   // customize the file search/serving algorithm for this particular website
104   CustomSite.prototype.respond = async function(env) {
105     if (
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'
110     ) {
111       this.die(env, `banned file ${env.parsed_url.pathname}`)
112       return
113     }
114     return /*await*/ _jst_server.Site.prototype.respond.call(this, env)
115   }
116
117   return new CustomSite(
118     resources,
119     root,
120     {},
121     prev_site
122   )
123 }