UNPKG

alpha-one

Version:

ideas about recurring tasks in Web- and Backend-Application building

202 lines (190 loc) 10.1 kB
############################################################################################################ # 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