Move *.jst from / to /_lib to keep things tidy, implement /_lib/Problem.jst and ...
authorNick Downing <nick@ndcode.org>
Fri, 14 Jan 2022 11:19:18 +0000 (22:19 +1100)
committerNick Downing <nick@ndcode.org>
Fri, 14 Jan 2022 11:23:56 +0000 (22:23 +1100)
19 files changed:
_lib/Problem.jst [new file with mode: 0644]
_lib/breadcrumbs.jst [moved from breadcrumbs.jst with 100% similarity]
_lib/navbar.jst [moved from navbar.jst with 99% similarity]
_lib/page.jst [moved from page.jst with 98% similarity]
_lib/post_request.jst [new file with mode: 0644]
_lib/session_cookie.jst [moved from session_cookie.jst with 100% similarity]
api/errors.json
api/sign_up.js.min [deleted file]
api/sign_up.json.jst [deleted file]
api/sign_up/create_account.json.jst [new file with mode: 0644]
api/verification_image.png.jst
contact/index.html.jst
index.html.jst
js/api_call.js.min [new file with mode: 0644]
jsdoc.dir.jst
my_account/index.html.jst
my_account/sign_up/index.html.jst
search/index.html.jst
sphinx.dir.jst

diff --git a/_lib/Problem.jst b/_lib/Problem.jst
new file mode 100644 (file)
index 0000000..e403795
--- /dev/null
@@ -0,0 +1,9 @@
+class Problem {
+  constructor(title, detail, status) {
+    this.title = title
+    this.detail = detail
+    this.status = status
+  }
+}
+
+return Problem
similarity index 100%
rename from breadcrumbs.jst
rename to _lib/breadcrumbs.jst
similarity index 99%
rename from navbar.jst
rename to _lib/navbar.jst
index fea0c4a..8498bb0 100644 (file)
@@ -8,7 +8,7 @@ return async (env, head, body, scripts) => {
   let icon_search_mono = await env.site.get_min_svg('/_svg/icon_search_mono.svg')
   let logo_large = await env.site.get_min_svg('/_svg/logo_large.svg')
   let menu = await env.site.get_menu('/_menu.json')
-  let page = await _require('/page.jst')
+  let page = await _require('/_lib/page.jst')
   //let session = await _require('/session.jst')
 
   // initialize env.sessions, env.session_key, env.session
similarity index 98%
rename from page.jst
rename to _lib/page.jst
index 7345f29..058ae05 100644 (file)
--- a/page.jst
@@ -5,7 +5,7 @@ let crypto = require('crypto')
 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')
-  let session_cookie = await _require('/session_cookie.jst')
+  let session_cookie = await _require('/_lib/session_cookie.jst')
 
   // initialize env.session_key, set cookie in env.response
   let transaction = await env.site.database.Transaction()
diff --git a/_lib/post_request.jst b/_lib/post_request.jst
new file mode 100644 (file)
index 0000000..a20e797
--- /dev/null
@@ -0,0 +1,75 @@
+let stream_buffers = require('stream-buffers')
+
+return async (env, api, func) => {
+  let Problem = await _require('/_lib/Problem.jst')
+
+  let result
+  try {
+    if (env.request.method !== 'POST') {
+      env.response.setHeader('Allow', 'POST')
+      throw new Problem(
+        'Method not allowed',
+        `The endpoint "${env.parsed_url.path}" requires a POST request.`,
+        405
+      )
+    }
+
+    let write_stream = new stream_buffers.WritableStreamBuffer()
+    let data = new Promise(
+      (resolve, reject) => {
+        write_stream.
+        on('finish', () => {resolve(write_stream.getContents())}).
+        on('error', () => {reject()})
+      }
+    )
+    env.request.pipe(write_stream)
+    let arguments = JSON.parse((await data).toString())
+    console.log('api', api, 'arguments', arguments)
+
+    result = await func(...arguments)
+    if (result === undefined)
+      result = null
+    console.log('api', api, 'result', result)
+  }
+  catch (error) {
+    let problem =
+      error instanceof Problem ?
+        error :
+        new Problem(
+          // title
+          'Internal server error',
+          // details
+          error.message,
+          // status
+          500
+        )
+    console.log('api', api, 'problem', problem.detail)
+
+    env.mime_type = 'application/problem+json; charset=utf-8'
+    env.site.serve(
+      env,
+      problem.status,
+      Buffer.from(
+        JSON.stringify(
+          {
+            title: problem.title,
+            detail: problem.detail,
+            status: problem.status
+          },
+          null,
+          2
+        ) + '\n',
+        'utf-8'
+      ),
+      'post_request.jst'
+    )
+    return
+  }
+  
+  env.site.serve(
+    env,
+    200,
+    Buffer.from(JSON.stringify(result, null, 2) + '\n', 'utf-8'),
+    'post_request.jst'
+  )
+}
similarity index 100%
rename from session_cookie.jst
rename to _lib/session_cookie.jst
index 28b7e37..fc1b415 100644 (file)
   "417": "Expectation failed",
   "418": "No verification image in session",
   "419": "Verification code mismatch",
-  "420": "Account already exists"
+  "420": "Account already exists",
+  "500": "Internal server error",
+  "501": "Not implemented",
+  "502": "Bad gateway",
+  "503": "Service unavailable",
+  "504": "Gateway timeout",
+  "505": "HTTP version not supported"
 }
diff --git a/api/sign_up.js.min b/api/sign_up.js.min
deleted file mode 100644 (file)
index e4140d4..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*let*/ sign_up = async (
-  email,
-  verification_code,
-  given_names,
-  family_name,
-  password,
-  contact_me
-) => {
-  let response = await fetch(
-    '/api/sign_up.json',
-    {
-      method: 'POST',
-      body: JSON.stringify(
-        {
-          email,
-          verification_code,
-          given_names,
-          family_name,
-          password,
-          contact_me
-        }
-      )
-    }
-  )
-  if (!response.ok)
-    throw new Error((await response.json()).detail)
-  return /*await*/ response.json()
-}
diff --git a/api/sign_up.json.jst b/api/sign_up.json.jst
deleted file mode 100644 (file)
index 12483f5..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-let stream_buffers = require('stream-buffers')
-let XDate = require('xdate')
-
-return async env => {
-  let session_cookie = await _require('/session_cookie.jst')
-
-  if (env.request.method !== 'POST') {
-    env.response.setHeader('Allow', 'POST')
-    env.mime_type = 'application/problem+json; charset=utf-8'
-    env.site.serve(
-      env,
-      405,
-      Buffer.from(
-        JSON.stringify(
-          {
-            title: 'Method not allowed',
-            detail: `The endpoint "${env.parsed_url.path}" requires a POST request.`,
-            status: 405
-          },
-          null,
-          2
-        ) + '\n',
-        'utf-8'
-      ),
-      'sign_up.json.jst'
-    )
-    return
-  }
-
-  let write_stream = new stream_buffers.WritableStreamBuffer()
-  let data = new Promise(
-    (resolve, reject) => {
-      write_stream.
-      on('finish', () => {resolve(write_stream.getContents())}).
-      on('error', () => {reject()})
-    }
-  )
-  env.request.pipe(write_stream)
-  let query = JSON.parse((await data).toString())
-  let email = query.email.toLowerCase()
-  console.log('sign up', email)
-
-  // initialize env.session_key, set cookie in env.response
-  let transaction = await env.site.database.Transaction()
-  let session = await session_cookie(env, transaction)
-
-  let captcha = await session.get('captcha')
-  if (captcha === undefined || XDate.now() >= captcha.get('expires')) {
-    transaction.rollback()
-
-    env.mime_type = 'application/problem+json; charset=utf-8'
-    env.site.serve(
-      env,
-      418,
-      Buffer.from(
-        JSON.stringify(
-          {
-            title: 'No verification image in session',
-            detail: `Please call the "/api/verification_image.png" endpoint to create a verification image, in same session as the "/api/sign_up.json" call and less than one hour prior.`,
-            status: 418
-          },
-          null,
-          2
-        ) + '\n',
-        'utf-8'
-      ),
-      'sign_up.json.jst'
-    )
-    return
-  }
-
-  
-  let verification_code = query.verification_code.toLowerCase()
-  let captcha_text = await captcha.get('text')
-  if (verification_code !== captcha_text) {
-    console.log(`verification code mismatch, \"${verification_code}\" should be \"${captcha_text}\"`)
-    transaction.rollback()
-
-    env.mime_type = 'application/problem+json; charset=utf-8'
-    env.site.serve(
-      env,
-      419,
-      Buffer.from(
-        JSON.stringify(
-          {
-            title: 'Verification code mismatch',
-            detail: `The provided verification code "${verification_code}" did not match the verification image.`,
-            status: 419
-          },
-          null,
-          2
-        ) + '\n',
-        'utf-8'
-      ),
-      'sign_up.json.jst'
-    )
-    return
-  }
-
-  let accounts = await (
-    await transaction.get({})
-  ).get('accounts', {})
-
-  if (accounts.has(email)) {
-    transaction.rollback()
-
-    env.mime_type = 'application/problem+json; charset=utf-8'
-    env.site.serve(
-      env,
-      420,
-      Buffer.from(
-        JSON.stringify(
-          {
-            title: 'Account already exists',
-            detail: `The email "${email}" already has an account registered.`,
-            status: 420
-          },
-          null,
-          2
-        ) + '\n',
-        'utf-8'
-      ),
-      'sign_up.json.jst'
-    )
-    return
-  }
-  accounts.set(
-    email,
-    transaction.json_to_logjson(
-      {
-        given_names: query.given_names || '',
-        family_name: query.family_name || '',
-        password: query.password || '',
-        contact_me: query.contact_me || false,
-        email_verified: false
-      }
-    )
-  )
-
-  await transaction.commit()
-
-  env.site.serve(
-    env,
-    200,
-    Buffer.from(
-      JSON.stringify(
-        null,
-        null,
-        2
-      ) + '\n',
-      'utf-8'
-    ),
-    'sign_up.json.jst'
-  )
-}
diff --git a/api/sign_up/create_account.json.jst b/api/sign_up/create_account.json.jst
new file mode 100644 (file)
index 0000000..ba00760
--- /dev/null
@@ -0,0 +1,69 @@
+let XDate = require('xdate')
+
+return async env => {
+  let post_request = await _require('/_lib/post_request.jst')
+  let session_cookie = await _require('/_lib/session_cookie.jst')
+  let Problem = await _require('/_lib/Problem.jst')
+
+  post_request(
+    // env
+    env,
+    // api
+    '/api/sign_up/create_account',
+    // func
+    async (verification_code, details) => {
+      // coerce and/or validate
+      verification_code = verification_code.slice(0, 6).toLowerCase()
+      details = {
+        email: details.email.slice(0, 256).toLowerCase(),
+        given_names: details.given_names.slice(0, 256),
+        family_name: details.family_name.slice(0, 256),
+        password: details.password.slice(0, 256),
+        contact_me: details.contact_me ? true : false
+      }
+
+      let transaction = await env.site.database.Transaction()
+      try {
+        // initialize env.session_key, set cookie in env.response
+        let session = await session_cookie(env, transaction)
+
+        let captcha = await session.get('captcha')
+        if (captcha === undefined || XDate.now() >= captcha.get('expires'))
+          throw new Problem(
+            'No verification image in session',
+            `Please call the "/api/verification_image.png" endpoint to create a verification image, in same session as the "/api/sign_up.json" call and less than one hour prior.`,
+            418
+          )
+        
+        let captcha_text = await captcha.get('text')
+        if (verification_code !== captcha_text) {
+          console.log(`verification code mismatch, \"${verification_code}\" should be \"${captcha_text}\"`)
+
+          throw new Problem(
+            'Verification code mismatch',
+            `The provided verification code "${verification_code}" did not match the verification image.`,
+            419
+          )
+        }
+
+        let accounts = await (
+          await transaction.get({})
+        ).get('accounts', {})
+
+        if (accounts.has(details.email))
+          throw new Problem(
+            'Account already exists',
+            `The email "${details.email}" already has an account registered.`,
+            420
+          )
+        accounts.set(details.email, transaction.json_to_logjson(details))
+
+        await transaction.commit()
+      }
+      catch (error) {
+        transaction.rollback()
+        throw error
+      }
+    }
+  )
+}
index db41565..7d0bfea 100644 (file)
@@ -3,7 +3,7 @@ let util = require('util')
 let XDate = require('xdate')
 
 return async env => {
-  let session_cookie = await _require('/session_cookie.jst')
+  let session_cookie = await _require('/_lib/session_cookie.jst')
 
   let captcha = captchagen.create()
   captcha.generate()
index 3b89d33..2216fc6 100644 (file)
@@ -3,9 +3,9 @@ let stream_buffers = require('stream-buffers')
 let XDate = require('xdate')
 
 return async env => {
-  let breadcrumbs = await _require('/breadcrumbs.jst')
+  let breadcrumbs = await _require('/_lib/breadcrumbs.jst')
   let globals = await env.site.get_json('/_config/globals.json')
-  let navbar = await _require('/navbar.jst')
+  let navbar = await _require('/_lib/navbar.jst')
 
   await navbar(
     env,
index 118c236..c34c82d 100644 (file)
@@ -1,7 +1,7 @@
 return async env => {
-  let breadcrumbs = await _require('/breadcrumbs.jst')
+  let breadcrumbs = await _require('/_lib/breadcrumbs.jst')
   let icon_jst = await env.site.get_min_svg('/_svg/icon_jst.svg')
-  let navbar = await _require('/navbar.jst')
+  let navbar = await _require('/_lib/navbar.jst')
   let icon_pitree = await env.site.get_min_svg('/_svg/icon_pitree.svg')
  
   await navbar(
diff --git a/js/api_call.js.min b/js/api_call.js.min
new file mode 100644 (file)
index 0000000..7ddced8
--- /dev/null
@@ -0,0 +1,12 @@
+api_call = async (endpoint, ...arguments) => {
+  let response = await fetch(
+    endpoint,
+    {
+      method: 'POST',
+      body: JSON.stringify(arguments)
+    }
+  )
+  if (!response.ok)
+    throw new Error((await response.json()).detail)
+  return /*await*/ response.json()
+}
index b22ef9f..35f8d86 100644 (file)
@@ -20,7 +20,7 @@ let serve_html = async (env, pathname) => {
   }
 
   // insert title and body into our navbar
-  let navbar = await _require('/navbar.jst')
+  let navbar = await _require('/_lib/navbar.jst')
   await navbar(
     env,
     // head
index 42331ee..0e44a6c 100644 (file)
@@ -3,9 +3,9 @@ let stream_buffers = require('stream-buffers')
 
 return async env => {
   //let account = await _require('/account.jst')
-  let breadcrumbs = await _require('/breadcrumbs.jst')
+  let breadcrumbs = await _require('/_lib/breadcrumbs.jst')
   let menu = await env.site.get_menu('/my_account/_menu.json')
-  let navbar = await _require('/navbar.jst')
+  let navbar = await _require('/_lib/navbar.jst')
 
   await navbar(
     env,
index 9bb63e7..8eaff34 100644 (file)
@@ -1,8 +1,8 @@
 return async env => {
-  let breadcrumbs = await _require('/breadcrumbs.jst')
+  let breadcrumbs = await _require('/_lib/breadcrumbs.jst')
   let icon_cross = await env.site.get_min_svg('/_svg/icon_cross.svg')
   let icon_tick = await env.site.get_min_svg('/_svg/icon_tick.svg')
-  let navbar = await _require('/navbar.jst')
+  let navbar = await _require('/_lib/navbar.jst')
 
   await navbar(
     env,
@@ -155,9 +155,14 @@ return async env => {
     },
     // scripts
     async _out => {
-      script(src="/api/sign_up.js") {}
+      script(src="/js/api_call.js") {}
 
       script {
+        let sign_up_create_account = async (...arguments) => api_call(
+          '/api/sign_up/create_account.json',
+          ...arguments
+        )
+
         $(document).ready(
           () => {
             $('#step-1-continue').click(
@@ -182,12 +187,17 @@ return async env => {
                 $('#step-2-cross').hide()
                 $('#step-2-spinner').show()
                 try {
-                  await sign_up(
-                    document.getElementById('email').value,
-                    document.getElementById('verification-code').value
-                    document.getElementById('given-names').value,
-                    document.getElementById('family-name').value,
-                    document.getElementById('password').value
+                  await sign_up_create_account(
+                    // verification_code
+                    document.getElementById('verification-code').value,
+                    // details
+                    {
+                      email: document.getElementById('email').value,
+                      given_names: document.getElementById('given-names').value,
+                      family_name: document.getElementById('family-name').value,
+                      password: document.getElementById('password').value,
+                      contact_me: document.getElementById('contact-me').value
+                    }
                   )
                 }
                 catch (e) {
index e80bbfe..b43beb1 100644 (file)
@@ -2,8 +2,8 @@ let assert = require('assert')
 let querystring = require('querystring')
 
 return async env => {
-  let breadcrumbs = await _require('/breadcrumbs.jst')
-  let navbar = await _require('/navbar.jst')
+  let breadcrumbs = await _require('/_lib/breadcrumbs.jst')
+  let navbar = await _require('/_lib/navbar.jst')
   let zet_site = await env.site.get_zettair('/_zet/site')
 
   let query = env.parsed_url.query.query
index 39baefb..0856b4e 100644 (file)
@@ -20,7 +20,7 @@ let serve_html = async (env, pathname) => {
   }
 
   // insert head before/after stylesheet and body into our navbar
-  let navbar = await _require('/navbar.jst')
+  let navbar = await _require('/_lib/navbar.jst')
   await navbar(
     env,
     // head