Change _config/server.jst to return a pure JSON object with information about the...
authorNick Downing <nick@ndcode.org>
Tue, 4 Dec 2018 00:47:01 +0000 (11:47 +1100)
committerNick Downing <nick@ndcode.org>
Tue, 4 Dec 2018 00:47:38 +0000 (11:47 +1100)
Server.js
Site.js
SiteConfig.js [deleted file]
SiteRedirect.js [deleted file]
SiteRoot.js [deleted file]
_config/server.jst
cli.js [moved from jst_server.js with 80% similarity]
index.js
package.json
site/_config/site.jst [new file with mode: 0644]

index 65dae19..e4304d7 100644 (file)
--- a/Server.js
+++ b/Server.js
@@ -16,18 +16,15 @@ let Server = function() {
   this.enable_caching = false
   this.listen = []
   this.mime_types = {}
-  this.sites = {}
+  this.hosts = {}
+  this.roots = {}
 
   this.mime_type_html = 'text/html; charset=utf-8'
   this.mime_type_default = 'application/octet-stream'
 
   this.resources = new Resources()
-  this.resources_jst_cache = this.resources.ref(
-    'jst_cache',
-    () => new Resources()
-  )
-  this.jst_cache = this.resources_jst_cache.ref(
-    '.',
+  this.jst_cache = this.resources.ref(
+    'jst_cache:.',
     () => new JSTCache('.', {_jst_server: jst_server}, true)
   )
 }
@@ -185,7 +182,7 @@ Server.prototype.refresh_config = async function() {
       this.mime_types['.html'] :
       this.mime_type_default
 
-    this.sites = config.sites
+    this.hosts = config.hosts
   }
 }
 
@@ -257,7 +254,7 @@ Server.prototype.respond = async function(request, response, protocol) {
     //console.log('parsed_url', parsed_url)
 
     if (
-      !Object.prototype.hasOwnProperty.call(this.sites, parsed_url.hostname)
+      !Object.prototype.hasOwnProperty.call(this.hosts, parsed_url.hostname)
     ) {
       this.die(
         response,
@@ -277,18 +274,52 @@ Server.prototype.respond = async function(request, response, protocol) {
         mime_type = this.mime_types[extension]
     }
 
-    let site = this.sites[parsed_url.hostname]
-    await site.respond(
-      {
-        mime_type: mime_type,
-        parsed_url: parsed_url,
-        pathname: parsed_url.pathname,
-        response: response,
-        request: request,
-        server: this
+    let host = this.hosts[parsed_url.hostname]
+    switch (host.type) {
+    case 'redirect':
+      let new_host = host.host
+      if (parsed_url.port !== undefined)
+        new_host += ':' + parsed_url.port
+      this.redirect(
+        response,
+        `${parsed_url.protocol}//${redirect}${request.url}`,
+        `redirecting ${parsed_url.host} to ${new_host}`
+      )
+      return
+    case 'site':
+      let root
+      if (Object.prototype.hasOwnProperty.call(this.roots, host.root))
+        root = this.roots[host.root]
+      else {
+        root = {
+          jst_cache: this.resources.ref(
+            `jst_cache:${host.root}`,
+            () => new JSTCache(host.root, {_jst_server: jst_server}, true)
+          ),
+          site: undefined
+        }
+        this.roots[host.root] = root
       }
-    )
-  }
+      let config = await root.jst_cache.get(
+        `${host.root}/_config/site.jst`,
+        true
+      )
+      if (config !== undefined)
+        root.site = await config(this, host.root, root.site)
+      return root.site.respond(
+        {
+          mime_type: mime_type,
+          parsed_url: parsed_url,
+          pathname: parsed_url.pathname,
+          response: response,
+          request: request,
+          server: this
+        }
+      )
+    default:
+      assert(false)
+    }
+  }     
   catch (err) {
     let message = (err.stack || err.message).toString()
     this.serve(
diff --git a/Site.js b/Site.js
index 33ab317..e5dd80f 100644 (file)
--- a/Site.js
+++ b/Site.js
@@ -1,10 +1,74 @@
-let Site = function(server) {
+let JSONCache = require('@ndcode/json_cache')
+let JSONCacheRW = require('@ndcode/json_cache_rw')
+let JSTCache = require('@ndcode/jst_cache')
+let LessCSSCache = require('@ndcode/less_css_cache')
+let MinCSSCache = require('@ndcode/min_css_cache')
+let MinJSCache = require('@ndcode/min_js_cache')
+let MinHTMLCache = require('@ndcode/min_html_cache')
+let MinSVGCache = require('@ndcode/min_svg_cache')
+let Resources = require('./Resources')
+let TextCache = require('@ndcode/text_cache')
+let ZipCache = require('@ndcode/zip_cache')
+let assert = require('assert')
+let fs = require('fs')
+let jst_server = require('./index')
+let util = require('util')
+
+let fs_mkdir = util.promisify(fs.mkdir)
+let fs_readFile = util.promisify(fs.readFile)
+let fs_stat = util.promisify(fs.stat)
+
+let Site = function(server, root) {
   if (!this instanceof Site)
     throw new Error('Site is a constructor')
   this.server = server
   this.socket_io_connect_listeners = [] // later will use this for destruction
+  this.root = root
+
+  this.json_cache = server.resources.ref(
+    'json_cache',
+    () => new JSONCache(true)
+  )
+  this.json_cache_rw = server.resources.ref(
+    'json_cache_rw',
+    () => new JSONCacheRW(true)
+  )
+  this.jst_cache = server.resources.ref(
+    `jst_cache:${root}`,
+    () => new JSTCache(root, {_jst_server: jst_server}, true)
+  )
+  this.less_css_cache = server.resources.ref(
+    `less_css_cache:${root}`,
+    () => new LessCSSCache(root, true)
+  )
+  this.min_css_cache = server.resources.ref(
+    'min_css_cache',
+    () => new MinCSSCache(true)
+  )
+  this.min_js_cache = server.resources.ref(
+    'min_js_cache',
+    () => new MinJSCache(true)
+  )
+  this.min_html_cache = server.resources.ref(
+    'min_html_cache',
+    () => new MinHTMLCache(true)
+  )
+  this.min_svg_cache = server.resources.ref(
+    'min_svg_cache',
+    () => new MinSVGCache(true)
+  )
+  this.text_cache = server.resources.ref(
+    'text_cache',
+    () => new TextCache(true)
+  )
+  this.zip_cache = server.resources.ref(
+    'zip_cache',
+    () => new ZipCache(true)
+  )
 }
 
+Site.prototype = Object.create(Site.prototype)
+
 Site.prototype.serve = function(env, status, data, message_from) {
   this.server.serve(
     env.response,
@@ -31,8 +95,318 @@ Site.prototype.redirect = function(env, pathname) {
   )
 }
 
+Site.prototype.internal_ensure_dir = async function(pathname) {
+  try {
+    await fs_mkdir(pathname)
+    console.log('create directory', pathname)
+  }
+  catch (err) {
+    if (err.code !== 'EEXIST') // should check error type
+      throw err
+  }
+}
+
+Site.prototype.get_json = function(pathname) {
+  return /*await*/ this.json_cache.get(this.root + pathname)
+}
+
+Site.prototype.get_jst = function(pathname) {
+  return /*await*/ this.jst_cache.get(this.root + pathname)
+}
+
+Site.prototype.get_less_css = function(pathname) {
+  return /*await*/ this.less_css_cache.get(this.root + pathname)
+}
+
+Site.prototype.get_min_css = function(pathname) {
+  return /*await*/ this.min_css_cache.get(this.root + pathname)
+}
+
+Site.prototype.get_min_html = function(pathname) {
+  return /*await*/ this.min_html_cache.get(this.root + pathname)
+}
+
+Site.prototype.get_min_js = function(pathname) {
+  return /*await*/ this.min_js_cache.get(this.root + pathname)
+}
+
+Site.prototype.get_min_svg = function(pathname) {
+  return /*await*/ this.min_svg_cache.get(this.root + pathname)
+}
+
+Site.prototype.get_text = function(pathname) {
+  return /*await*/ this.text_cache.get(this.root + pathname)
+}
+
+Site.prototype.get_zip = function(pathname) {
+  return /*await*/ this.zip_cache.get(this.root + pathname)
+}
+
+Site.prototype.ensure_dir = async function(pathname) {
+  return /*await*/ this.internal_ensure_dir(this.root + pathname)
+}
+
+// this is for read/write JSON files
+// they will not be reloaded from disk if modified
+Site.prototype.read_json = async function(pathname, default_value) {
+  return /*await*/ this.json_cache_rw.read(
+    this.root + pathname,
+    default_value
+  )
+}
+
+Site.prototype.write_json = async function(pathname, value, timeout) {
+  return /*await*/ this.json_cache_rw.write(
+    this.root + pathname,
+    value,
+    timeout
+  )
+}
+
+Site.prototype.modify_json =
+  async function(pathname, default_value, modify_func, timeout) {
+    return /*await*/ this.json_cache_rw.modify(
+      this.root + pathname,
+      default_value,
+      modify_func,
+      timeout
+    )
+  }
+
+Site.prototype.serve_jst = async function(env, pathname, ...args) {
+  let template
+  try {
+    template = await this.jst_cache.get(pathname)
+  }
+  catch (err) {
+    if (!(err instanceof Error) || err.code !== 'ENOENT')
+      throw err
+    return false
+  }
+  env.site = this
+  await template(env, ...args)
+  return true
+}
+
+Site.prototype.serve_less_css = async function(env, pathname) {
+  if (pathname.slice(-9) !== '.css.less')
+    return false
+  let data 
+  try {
+    data = await this.less_css_cache.get(pathname)
+  }
+  catch (err) {
+    if (!(err instanceof Error) || err.code !== 'ENOENT')
+      throw err
+    return false
+  }
+  this.serve(env, 200, data, 'less_css')
+  return true
+}
+
+Site.prototype.serve_min_css = async function(env, pathname) {
+  if (pathname.slice(-8) !== '.css.min')
+    return false
+  let data 
+  try {
+    data = await this.min_css_cache.get(pathname)
+  }
+  catch (err) {
+    if (!(err instanceof Error) || err.code !== 'ENOENT')
+      throw err
+    return false
+  }
+  this.serve(env, 200, data, 'min_css')
+  return true
+}
+
+Site.prototype.serve_min_html = async function(env, pathname) {
+  if (pathname.slice(-9) !== '.html.min')
+    return false
+  let data 
+  try {
+    data = await this.min_html_cache.get(pathname)
+  }
+  catch (err) {
+    if (!(err instanceof Error) || err.code !== 'ENOENT')
+      throw err
+    return false
+  }
+  this.serve(env, 200, data, 'min_html')
+  return true
+}
+
+Site.prototype.serve_min_js = async function(env, pathname) {
+  if (pathname.slice(-7) !== '.js.min')
+    return false
+  let data 
+  try {
+    data = await this.min_js_cache.get(pathname)
+  }
+  catch (err) {
+    if (!(err instanceof Error) || err.code !== 'ENOENT')
+      throw err
+    return false
+  }
+  this.serve(env, 200, data, 'min_js')
+  return true
+}
+
+Site.prototype.serve_min_svg = async function(env, pathname) {
+  if (pathname.slice(-8) !== '.svg.min')
+    return false
+  let data 
+  try {
+    data = await this.min_svg_cache.get(pathname)
+  }
+  catch (err) {
+    if (!(err instanceof Error) || err.code !== 'ENOENT')
+      throw err
+    return false
+  }
+  this.serve(env, 200, data, 'min_svg')
+  return true
+}
+
+Site.prototype.serve_fs = async function(env, pathname) {
+  let data 
+  try {
+    data = await fs_readFile(pathname)
+  }
+  catch (err) {
+    if (!(err instanceof Error) || err.code !== 'ENOENT')
+      throw err
+    return false
+  }
+  this.serve(env, 200, data, 'fs')
+  return true
+}
+
+Site.prototype.serve_zip = async function(env, zipname, pathname) {
+  let zip 
+  try {
+    zip = await this.zip_cache.get(zipname)
+  }
+  catch (err) {
+    if (!(err instanceof Error) || err.code !== 'ENOENT')
+      throw err
+    return false
+  }
+  if (!Object.prototype.hasOwnProperty.call(zip, pathname))
+    return false
+  this.serve(env, 200, zip[pathname], 'zip')
+  return true
+}
+
+Site.prototype.serve_file = async function(env, pathname) {
+  //console.log(`serve_file ${pathname}`)
+  if (await this.serve_jst(env, pathname + '.jst'))
+    return
+  if (await this.serve_less_css(env, pathname + '.less'))
+    return
+  if (await this.serve_min_css(env, pathname + '.min'))
+    return
+  if (await this.serve_min_html(env, pathname + '.min'))
+    return
+  if (await this.serve_min_js(env, pathname + '.min'))
+    return
+  if (await this.serve_min_svg(env, pathname + '.min'))
+    return
+  if (await this.serve_fs(env, pathname))
+    return
+  this.die(env, `file not found ${env.pathname}`)
+}
+
+Site.prototype.serve_dir = async function(env, pathname, components) {
+  if (await this.serve_jst(env, pathname + '.dir.jst', components))
+    return
+
+  let stats
+  try {
+    stats = await fs_stat(pathname)
+  }
+  catch (err) {
+    if (!(err instanceof Error) || err.code !== 'ENOENT')
+      throw err
+    this.die(env, `directory not found ${pathname}`)
+    return
+  }
+  if (!stats.isDirectory()) {
+    this.die(
+      env,
+      components.length > 1 ?
+        `not directory ${pathname}` :
+        `unknown extension "${extension}" in ${env.pathname}`
+    )
+    return
+  }
+  return /*await*/ this.serve_path(env, pathname, components)
+}
+
+Site.prototype.serve_path = async function(env, pathname, components) {
+  //console.log(`serve_path ${pathname} ${components}`)
+  if (components.length === 0) {
+    // directory without trailing slash
+    this.redirect(env, env.pathname + '/index.html')
+    return
+  }
+
+  if (components[0].length === 0) {
+    if (components.length > 1)
+      this.die(env, `empty directory name in ${env.pathname}`)
+    else
+      // directory with trailing slash
+      this.redirect(env, env.pathname + 'index.html')
+    return
+  }
+
+  if (
+    components[0].charAt(0) === '.' ||
+    components[0].charAt(0) === '_'
+  ) {
+    this.die(env, `bad component "${components[0]}" in ${env.pathname}`)
+    return
+  }
+
+  let i = components[0].lastIndexOf('.')
+  if (i === -1)
+    i = components[0].length
+  let extension = components[0].slice(i)
+
+  pathname = `${pathname}/${components[0]}`
+  if (
+    extension.length !== 0 &&
+    Object.prototype.hasOwnProperty.call(this.server.mime_types, extension)
+  ) {
+    if (components.length > 1) {
+      this.die(env, `non-directory extension "${extension}" in ${env.pathname}`)
+      return
+    }
+    return /*await*/ this.serve_file(env, pathname)
+  }
+  return /*await*/ this.serve_dir(env, pathname, components.slice(1))
+}
+
 Site.prototype.respond = async function(env) {
-  throw new Error('not implemented')
+  if (
+    await this.serve_zip(
+      env,
+      this.root + '/_favicon/favicons.zip',
+      env.pathname
+    )
+  )
+    return
+  let components = env.pathname.split('/')
+  if (components.length) {
+    assert(components[0].length == 0)
+    components = components.slice(1)
+  }
+  return /*await*/ this.serve_path(env, this.root, components)
 }
 
 module.exports = Site
diff --git a/SiteConfig.js b/SiteConfig.js
deleted file mode 100644 (file)
index 7041ab4..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-let JSTCache = require('@ndcode/jst_cache')
-let Site = require('./Site')
-let jst_server = require('./index')
-
-let SiteConfig = function(server, root) {
-  if (!this instanceof SiteConfig)
-    throw new Error('SiteConfig is a constructor')
-  Site.call(this, server)
-  this.root = root
-
-  this.resources_jst_cache = server.resources.ref(
-    'jst_cache',
-    () => new Resources()
-  )
-  this.jst_cache = this.resources_jst_cache.ref(
-    root,
-    () => new JSTCache(root, {_jst_server: jst_server}, true)
-  )
-  this.site = undefined
-}
-
-SiteConfig.prototype = Object.create(Site.prototype)
-
-SiteConfig.prototype.respond = async function(env) {
-  let config = await this.jst_cache.get(
-    this.root + '/_config/site.jst',
-    true
-  )
-  if (config !== undefined)
-    this.site = await config(this.server, this.root, this.site)
-  return this.site.respond(env)
-}
-
-module.exports = SiteConfig
diff --git a/SiteRedirect.js b/SiteRedirect.js
deleted file mode 100644 (file)
index d012635..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-let Site = require('./Site')
-
-let SiteRedirect = function(server, redirect) {
-  if (!this instanceof SiteRedirect)
-    throw new Error('SiteRedirect is a constructor')
-  Site.call(this, server)
-  this.redirect = redirect
-}
-
-SiteRedirect.prototype = Object.create(Site.prototype)
-
-SiteRedirect.respond = async function(env) {
-  let new_host = this.redirect
-  if (parsed_url.port !== undefined)
-    new_host += ':' + parsed_url.port
-  this.redirect(
-    response,
-    `${parsed_url.protocol}//${redirect}${request.url}`,
-    `redirecting ${parsed_url.host} to ${new_host}`
-  )
-}
-
-module.exports = SiteRedirect
diff --git a/SiteRoot.js b/SiteRoot.js
deleted file mode 100644 (file)
index 45b2687..0000000
+++ /dev/null
@@ -1,363 +0,0 @@
-let JSONCache = require('@ndcode/json_cache')
-let JSONCacheRW = require('@ndcode/json_cache_rw')
-let JSTCache = require('@ndcode/jst_cache')
-let LessCSSCache = require('@ndcode/less_css_cache')
-let MinCSSCache = require('@ndcode/min_css_cache')
-let MinJSCache = require('@ndcode/min_js_cache')
-let MinHTMLCache = require('@ndcode/min_html_cache')
-let MinSVGCache = require('@ndcode/min_svg_cache')
-let Resources = require('./Resources')
-let Site = require('./Site')
-let TextCache = require('@ndcode/text_cache')
-let ZipCache = require('@ndcode/zip_cache')
-let assert = require('assert')
-let fs = require('fs')
-let jst_server = require('./index')
-let util = require('util')
-
-let fs_mkdir = util.promisify(fs.mkdir)
-let fs_readFile = util.promisify(fs.readFile)
-let fs_stat = util.promisify(fs.stat)
-
-let SiteRoot = function(server, root) {
-  if (!this instanceof SiteRoot)
-    throw new Error('SiteRoot is a constructor')
-  Site.call(this, server)
-  this.root = root
-
-  this.json_cache = new JSONCache(true)
-  this.json_cache_rw = new JSONCacheRW(true)
-  this.resources_jst_cache = server.resources.ref(
-    'jst_cache',
-    () => new Resources()
-  )
-  this.jst_cache = server.resources_jst_cache.ref(
-    root,
-    () => new JSTCache(root, {_jst_server: jst_server}, true)
-  )
-  this.less_css_cache = new LessCSSCache(root, true)
-  this.min_css_cache = new MinCSSCache(true)
-  this.min_js_cache = new MinJSCache(true)
-  this.min_html_cache = new MinHTMLCache(true)
-  this.min_svg_cache = new MinSVGCache(true)
-  this.text_cache = new TextCache(true)
-  this.zip_cache = new ZipCache(true)
-}
-
-SiteRoot.prototype = Object.create(Site.prototype)
-
-SiteRoot.prototype.internal_ensure_dir = async function(pathname) {
-  try {
-    await fs_mkdir(pathname)
-    console.log('create directory', pathname)
-  }
-  catch (err) {
-    if (err.code !== 'EEXIST') // should check error type
-      throw err
-  }
-}
-
-SiteRoot.prototype.get_json = function(pathname) {
-  return /*await*/ this.json_cache.get(this.root + pathname)
-}
-
-SiteRoot.prototype.get_jst = function(pathname) {
-  return /*await*/ this.jst_cache.get(this.root + pathname)
-}
-
-SiteRoot.prototype.get_less_css = function(pathname) {
-  return /*await*/ this.less_css_cache.get(this.root + pathname)
-}
-
-SiteRoot.prototype.get_min_css = function(pathname) {
-  return /*await*/ this.min_css_cache.get(this.root + pathname)
-}
-
-SiteRoot.prototype.get_min_html = function(pathname) {
-  return /*await*/ this.min_html_cache.get(this.root + pathname)
-}
-
-SiteRoot.prototype.get_min_js = function(pathname) {
-  return /*await*/ this.min_js_cache.get(this.root + pathname)
-}
-
-SiteRoot.prototype.get_min_svg = function(pathname) {
-  return /*await*/ this.min_svg_cache.get(this.root + pathname)
-}
-
-SiteRoot.prototype.get_text = function(pathname) {
-  return /*await*/ this.text_cache.get(this.root + pathname)
-}
-
-SiteRoot.prototype.get_zip = function(pathname) {
-  return /*await*/ this.zip_cache.get(this.root + pathname)
-}
-
-SiteRoot.prototype.ensure_dir = async function(pathname) {
-  return /*await*/ this.internal_ensure_dir(this.root + pathname)
-}
-
-// this is for read/write JSON files
-// they will not be reloaded from disk if modified
-SiteRoot.prototype.read_json = async function(pathname, default_value) {
-  return /*await*/ this.json_cache_rw.read(
-    this.root + pathname,
-    default_value
-  )
-}
-
-SiteRoot.prototype.write_json = async function(pathname, value, timeout) {
-  return /*await*/ this.json_cache_rw.write(
-    this.root + pathname,
-    value,
-    timeout
-  )
-}
-
-SiteRoot.prototype.modify_json =
-  async function(pathname, default_value, modify_func, timeout) {
-    return /*await*/ this.json_cache_rw.modify(
-      this.root + pathname,
-      default_value,
-      modify_func,
-      timeout
-    )
-  }
-
-SiteRoot.prototype.serve_jst = async function(env, pathname, ...args) {
-  let template
-  try {
-    template = await this.jst_cache.get(pathname)
-  }
-  catch (err) {
-    if (!(err instanceof Error) || err.code !== 'ENOENT')
-      throw err
-    return false
-  }
-  env.site = this
-  await template(env, ...args)
-  return true
-}
-
-SiteRoot.prototype.serve_less_css = async function(env, pathname) {
-  if (pathname.slice(-9) !== '.css.less')
-    return false
-  let data 
-  try {
-    data = await this.less_css_cache.get(pathname)
-  }
-  catch (err) {
-    if (!(err instanceof Error) || err.code !== 'ENOENT')
-      throw err
-    return false
-  }
-  this.serve(env, 200, data, 'less_css')
-  return true
-}
-
-SiteRoot.prototype.serve_min_css = async function(env, pathname) {
-  if (pathname.slice(-8) !== '.css.min')
-    return false
-  let data 
-  try {
-    data = await this.min_css_cache.get(pathname)
-  }
-  catch (err) {
-    if (!(err instanceof Error) || err.code !== 'ENOENT')
-      throw err
-    return false
-  }
-  this.serve(env, 200, data, 'min_css')
-  return true
-}
-
-SiteRoot.prototype.serve_min_html = async function(env, pathname) {
-  if (pathname.slice(-9) !== '.html.min')
-    return false
-  let data 
-  try {
-    data = await this.min_html_cache.get(pathname)
-  }
-  catch (err) {
-    if (!(err instanceof Error) || err.code !== 'ENOENT')
-      throw err
-    return false
-  }
-  this.serve(env, 200, data, 'min_html')
-  return true
-}
-
-SiteRoot.prototype.serve_min_js = async function(env, pathname) {
-  if (pathname.slice(-7) !== '.js.min')
-    return false
-  let data 
-  try {
-    data = await this.min_js_cache.get(pathname)
-  }
-  catch (err) {
-    if (!(err instanceof Error) || err.code !== 'ENOENT')
-      throw err
-    return false
-  }
-  this.serve(env, 200, data, 'min_js')
-  return true
-}
-
-SiteRoot.prototype.serve_min_svg = async function(env, pathname) {
-  if (pathname.slice(-8) !== '.svg.min')
-    return false
-  let data 
-  try {
-    data = await this.min_svg_cache.get(pathname)
-  }
-  catch (err) {
-    if (!(err instanceof Error) || err.code !== 'ENOENT')
-      throw err
-    return false
-  }
-  this.serve(env, 200, data, 'min_svg')
-  return true
-}
-
-SiteRoot.prototype.serve_fs = async function(env, pathname) {
-  let data 
-  try {
-    data = await fs_readFile(pathname)
-  }
-  catch (err) {
-    if (!(err instanceof Error) || err.code !== 'ENOENT')
-      throw err
-    return false
-  }
-  this.serve(env, 200, data, 'fs')
-  return true
-}
-
-SiteRoot.prototype.serve_zip = async function(env, zipname, pathname) {
-  let zip 
-  try {
-    zip = await this.zip_cache.get(zipname)
-  }
-  catch (err) {
-    if (!(err instanceof Error) || err.code !== 'ENOENT')
-      throw err
-    return false
-  }
-  if (!Object.prototype.hasOwnProperty.call(zip, pathname))
-    return false
-  this.serve(env, 200, zip[pathname], 'zip')
-  return true
-}
-
-SiteRoot.prototype.serve_file = async function(env, pathname) {
-  //console.log(`serve_file ${pathname}`)
-  if (await this.serve_jst(env, pathname + '.jst'))
-    return
-  if (await this.serve_less_css(env, pathname + '.less'))
-    return
-  if (await this.serve_min_css(env, pathname + '.min'))
-    return
-  if (await this.serve_min_html(env, pathname + '.min'))
-    return
-  if (await this.serve_min_js(env, pathname + '.min'))
-    return
-  if (await this.serve_min_svg(env, pathname + '.min'))
-    return
-  if (await this.serve_fs(env, pathname))
-    return
-  this.die(env, `file not found ${env.pathname}`)
-}
-
-SiteRoot.prototype.serve_dir = async function(env, pathname, components) {
-  if (await this.serve_jst(env, pathname + '.dir.jst', components))
-    return
-
-  let stats
-  try {
-    stats = await fs_stat(pathname)
-  }
-  catch (err) {
-    if (!(err instanceof Error) || err.code !== 'ENOENT')
-      throw err
-    this.die(env, `directory not found ${pathname}`)
-    return
-  }
-  if (!stats.isDirectory()) {
-    this.die(
-      env,
-      components.length > 1 ?
-        `not directory ${pathname}` :
-        `unknown extension "${extension}" in ${env.pathname}`
-    )
-    return
-  }
-  return /*await*/ this.serve_path(env, pathname, components)
-}
-
-SiteRoot.prototype.serve_path = async function(env, pathname, components) {
-  //console.log(`serve_path ${pathname} ${components}`)
-  if (components.length === 0) {
-    // directory without trailing slash
-    this.redirect(env, env.pathname + '/index.html')
-    return
-  }
-
-  if (components[0].length === 0) {
-    if (components.length > 1)
-      this.die(env, `empty directory name in ${env.pathname}`)
-    else
-      // directory with trailing slash
-      this.redirect(env, env.pathname + 'index.html')
-    return
-  }
-
-  if (
-    components[0].charAt(0) === '.' ||
-    components[0].charAt(0) === '_'
-  ) {
-    this.die(env, `bad component "${components[0]}" in ${env.pathname}`)
-    return
-  }
-
-  let i = components[0].lastIndexOf('.')
-  if (i === -1)
-    i = components[0].length
-  let extension = components[0].slice(i)
-
-  pathname = `${pathname}/${components[0]}`
-  if (
-    extension.length !== 0 &&
-    Object.prototype.hasOwnProperty.call(this.server.mime_types, extension)
-  ) {
-    if (components.length > 1) {
-      this.die(env, `non-directory extension "${extension}" in ${env.pathname}`)
-      return
-    }
-    return /*await*/ this.serve_file(env, pathname)
-  }
-  return /*await*/ this.serve_dir(env, pathname, components.slice(1))
-}
-
-SiteRoot.prototype.respond = async function(env) {
-  if (
-    await this.serve_zip(
-      env,
-      this.root + '/_favicon/favicons.zip',
-      env.pathname
-    )
-  )
-    return
-  let components = env.pathname.split('/')
-  if (components.length) {
-    assert(components[0].length == 0)
-    components = components.slice(1)
-  }
-  return /*await*/ this.serve_path(env, this.root, components)
-}
-
-module.exports = SiteRoot
index 542bb8e..1a67aae 100644 (file)
@@ -2,8 +2,13 @@ return async server => (
   {
     "enable_caching": false,
     "listen": [
-      {"port": 8080},
-      {"port": 8443, "protocol": "https:"}
+      {
+        "port": 8080
+      },
+      {
+        "port": 8443,
+        "protocol": "https:"
+      }
     ],
     "mime_types": {
       ".css": "text/css; charset=utf-8",
@@ -17,9 +22,15 @@ return async server => (
       ".svg": "image/svg+xml",
       ".xml": "text/xml; charset=utf-8"
     },
-    sites: {
-      'localhost': new _jst_server.SiteRoot(server, 'site'),
-      'localhost.localdomain': new _jst_server.SiteRedirect(server, 'localhost')
+    hosts: {
+      "localhost": {
+        "type": "site",
+        "root": "site"
+      },
+      "localhost.localdomain": {
+        "type": "redirect",
+        "host": "localhost"
+      }
     }
   }
 )
similarity index 80%
rename from jst_server.js
rename to cli.js
index de71534..52c6ad2 100755 (executable)
+++ b/cli.js
@@ -1,5 +1,7 @@
 #!/usr/bin/env node
 
+// must load index first, to avoid circular dependency issue
+let index = require('./index')
 let Server = require('./Server')
 
 let server = new Server()
index 80ee574..ebd8970 100644 (file)
--- a/index.js
+++ b/index.js
@@ -1,6 +1,3 @@
 exports.Resources = require('./Resources')
 exports.Server = require('./Server')
 exports.Site = require('./Site')
-exports.SiteConfig = require('./SiteConfig')
-exports.SiteRedirect = require('./SiteRedirect')
-exports.SiteRoot = require('./SiteRoot')
index 4113238..b9ecf3c 100644 (file)
@@ -28,6 +28,6 @@
   "author": "Nick Downing",
   "license": "GPL-3.0",
   "bin": {
-    "jst_server": "jst_server.js"
+    "jst_server": "cli.js"
   }
 }
diff --git a/site/_config/site.jst b/site/_config/site.jst
new file mode 100644 (file)
index 0000000..0240704
--- /dev/null
@@ -0,0 +1 @@
+return async (server, root, prev_site) => new _jst_server.Site(server, root)