alpha-one
Version:
ideas about recurring tasks in Web- and Backend-Application building
202 lines (190 loc) • 10.1 kB
text/coffeescript
############################################################################################################
# ERROR = require 'coffeenode-stacktrace'
# njs_util = require 'util'
njs_path = require 'path'
# njs_fs = require 'fs'
#...........................................................................................................
OPTIONS = require 'coffeenode-options'
TYPES = require 'coffeenode-types'
TRM = require 'coffeenode-trm'
rpr = TRM.rpr.bind TRM
badge = 'α1/main'
log = TRM.get_logger 'plain', badge
info = TRM.get_logger 'info', badge
whisper = TRM.get_logger 'whisper', badge
alert = TRM.get_logger 'alert', badge
debug = TRM.get_logger 'debug', badge
warn = TRM.get_logger 'warn', badge
help = TRM.get_logger 'help', badge
echo = TRM.echo.bind TRM
#...........................................................................................................
express = require 'express'
@HTTP = require './http'
#...........................................................................................................
app_info = OPTIONS.get_app_info()
### Name used to store info on the `request` object: ###
# app_key = app_info[ 'name' ]
#===========================================================================================================
# MIDDLEWARE
#-----------------------------------------------------------------------------------------------------------
@get_request_counter = ->
request_count = 0
return ( request, response, next ) =>
request[ 'count' ] = ( request_count += 1 )
next()
#-----------------------------------------------------------------------------------------------------------
@show_debug_info = ->
return ( request, response, next ) =>
log TRM.blue 'show_debug_info'
debug 'session ID:', request[ 'sessionID' ]
debug 'cookies:', request[ 'cookies' ]
if ( session_cookie = request[ 'session' ]?[ 'cookie' ] )?
debug 'session cookie:', session_cookie
else
whisper 'no session or no cookie in session'
info request[ 'body' ]
# # debug request[ 'session' ][ 'cookie' ][ 'connect' ]?[ 'sid' ]
# debug "RQ##{request[ 'count' ]}"
next()
#-----------------------------------------------------------------------------------------------------------
@show_sid = ( secret ) ->
throw new Error "middleware `show_sid` must be called with the secret session key" unless secret?
signature = require 'cookie-signature'
prefix = 's:'
return ( request, response, next ) =>
debug '©38z', request[ 'cookies' ]
sid = request[ 'cookies' ][ 'connect.sid' ]
if sid? then info "client has SID", rpr signature.unsign ( sid.replace prefix, '' ), secret
else info "client has no SID"
next()
#-----------------------------------------------------------------------------------------------------------
@add_request_options = ->
### This middleware adds a plain old dictionary to the request object as `request[ 'alpha-one' ]`; other
middleware methods may use it to store values and add methods. ###
return ( request, response, next ) =>
throw new Error "app key 'A1' already present on request" if request[ 'A1' ]?
request[ 'A1' ] = {}
@HTTP._add_options request, response
#.......................................................................................................
next()
#-----------------------------------------------------------------------------------------------------------
@page_modifier = ->
#.........................................................................................................
return ( request, response, next ) =>
#.......................................................................................................
request.on 'page ready', =>
debug "page ready"
#.......................................................................................................
marks = [
'head-top'
'head-bottom'
'body-top'
'body-bottom' ]
#.......................................................................................................
for mark in marks
matcher = '<!--#'.concat mark, '-->'
method_name = 'add-to-'.concat mark
do ( mark, matcher, method_name ) ->
method = ( text ) ->
replacement = if /-top$/.test method_name then matcher.concat text else text.concat matcher
@[ 'A1' ][ 'page' ] = @[ 'A1' ][ 'page' ].replace matcher, replacement
request[ 'A1' ][ method_name ] = method.bind request
#.......................................................................................................
next()
#-----------------------------------------------------------------------------------------------------------
@flash_notifications = =>
#.........................................................................................................
flash_messages = []
#.........................................................................................................
return ( request, response, next ) =>
#.......................................................................................................
request.on 'page ready', =>
response.cookie 'flash-messages', ( JSON.stringify flash_messages ), path: '*'
flash_messages.length = 0
#.......................................................................................................
request[ 'A1' ][ 'flash-messages' ] = flash_messages
request[ 'A1' ][ 'flash' ] = ( title, text ) ->
message = [ title, text, ]
flash_messages.push message
# request.emit flash_key, message
#.......................................................................................................
next()
#-----------------------------------------------------------------------------------------------------------
@get_view_for = ( templates ) ->
return ( name, options ) =>
options ?= {}
remember_location = options[ 'remember-location' ] ? yes
#.......................................................................................................
return ( request, response ) =>
content_already_done = no
page_already_done = no
#.....................................................................................................
### TAINT code duplication; also used by @HTTP.bounce ###
response.cookie 'comes-from', request[ 'url' ], path: '*' if remember_location
#.....................................................................................................
on_error = ( error ) =>
alert error.stack
### TAINT should use dedicated method or static page ###
@HTTP.server_error request, response unless response.headerSent?
response.write "an error has occurred"
response.end()
#.....................................................................................................
content_done = ( content ) =>
log TRM.blue 'content_done'
throw new Error "content already finished; cannot call `done()` anymore" if content_already_done
content_already_done = yes
if content?
return on_error content if TYPES.isa_jserror content
page = templates.layout request, response, content, page_done
if page?
TYPES.validate_isa_text page
page_done page
else
page_done()
return null
#.....................................................................................................
page_done = ( page ) =>
log TRM.blue 'page_done'
throw new Error "page already finished; cannot call `done()` anymore" if page_already_done
page_already_done = yes
if ( request.listeners 'page ready' ).length > 0
request[ 'A1' ][ 'page' ] = page
request.emit 'page ready'
page = request[ 'A1' ][ 'page' ]
return on_error page if TYPES.isa_jserror page
@HTTP.write_header request, response
# debug '©22a', response.headerSent
response.write page if page?
response.end()
return null
#.....................................................................................................
try
content = templates[ name ] request, response, content_done
# debug '©4e', rpr content
if content?
TYPES.validate_isa_text content
content_done content
#.....................................................................................................
catch error
on_error error
#.....................................................................................................
return null
#-----------------------------------------------------------------------------------------------------------
@get_restrict_view_for = ( templates ) ->
return ( condition, name ) =>
throw new Error "argument 'condition' not yet supported" unless condition is 'user'
view_method = ( @get_view_for templates ) name
#.......................................................................................................
return ( request, response, done ) =>
if ( session = request[ 'session' ] )?
if session[ 'user' ]?
log TRM.blue 'restrict_view: ok'
return view_method request, response, done
else
session[ 'error' ] = "Access denied!"
log TRM.blue 'restrict_view: not ok'
@HTTP.bounce request, response, "/login"
@HTTP.write_header request, response
debug '©23a', response.headerSent
response.end()
return null