alpha-one
Version:
ideas about recurring tasks in Web- and Backend-Application building
415 lines (381 loc) • 20.4 kB
text/coffeescript
############################################################################################################
USERDB = require 'coffeenode-userdb'
OPTIONS = require 'coffeenode-options'
TRM = require 'coffeenode-trm'
rpr = TRM.rpr.bind TRM
badge = 'α1/templates'
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
BITSNPIECES = require 'coffeenode-bitsnpieces'
#...........................................................................................................
TEACUP = require 'coffeenode-teacup'
#...........................................................................................................
app_info = OPTIONS.get_app_info()
app_key = app_info[ 'name' ]
#...........................................................................................................
A1 = require './main'
user_db = USERDB.new_db()
#...........................................................................................................
USERDB.validate_is_running user_db
#===========================================================================================================
# TEACUP NAMESPACE ACQUISITION
#-----------------------------------------------------------------------------------------------------------
for name_ of TEACUP
eval "#{name_} = TEACUP[ #{rpr name_} ]"
#===========================================================================================================
# LAYOUT
#-----------------------------------------------------------------------------------------------------------
# TEMPLATES = {}
@layout = ( request, response, content, done ) ->
log TRM.blue 'layout'
return @[ request[ 'A1' ]?[ 'layout' ] ? 'plain' ] request, response, content, done
#-----------------------------------------------------------------------------------------------------------
@plain = ( request, response, content, done ) ->
log TRM.blue 'plain'
O = request[ 'A1' ]
page_style = O[ 'page-style' ] ? 'plain'
title = O[ 'title' ] ? 'welcome'
session = request[ 'session' ]
has_session = session?
if has_session
user = session[ 'user' ]
logged_in = user?
else
user = null
logged_in = no
#.........................................................................................................
return render =>
DOCTYPE 5
HTML =>
#.....................................................................................................
HEAD =>
COMMENT '#head-top'
META charset: 'utf-8'
TITLE title
RAW '<!--[if lt IE 9]>'
SCRIPT src: '/common/jquery.com/jquery-1.10.2.js'
RAW '<![endif]--><!--[if gte IE 9]><!-->'
SCRIPT src: '/common/jquery.com/jquery-2.0.3.js'
RAW '<!--<![endif]-->'
#===================================================================================================
# Client-Side Cookies
#...................................................................................................
SCRIPT src: '/common/github.com_carhartl_jquery-cookie/jquery.cookie.js'
#===================================================================================================
# Notifications
#...................................................................................................
### https://github.com/ehynds/jquery-notify ###
### http://www.erichynds.com/blog/a-jquery-ui-growl-ubuntu-notification-widget ###
SCRIPT src: '/common/jquery.com/jquery-ui-1.10.3/ui/jquery.ui.widget.js'
SCRIPT src: '/common/erichynds.com/jquery-notify/src/jquery.notify.js'
LINK rel: 'stylesheet', href: '/common/erichynds.com/jquery-notify/ui.notify.css'
RAW """
<style>
.ui-notify-message h1 {
font-size: 120%;
font-weight: normal;
font-style: italic;
}
</style>"""
COFFEESCRIPT ->
#.................................................................................................
after = ( seconds, method ) -> setTimeout method, seconds * 1000
#.................................................................................................
notification_options =
sticky: no
click: ( event, notification ) -> notification.close()
#.................................................................................................
notify = ( title, text ) ->
message =
title: title
text: text
#...............................................................................................
( $ '#notify-wrap' ).notify 'create',
'notify-default'
message
notification_options
#.................................................................................................
( $ 'document' ).ready ->
################################################################################################
( $ '#notify-wrap' ).notify
speed: 250 # i.e. effect duration
expires: 5000 # fades out after so many ms
################################################################################################
if ( flash_messages = $.cookie 'flash-messages' )?
flash_messages = JSON.parse flash_messages
for idx in [ flash_messages.length - 1 .. 0 ] by -1
[ title, text, ] = flash_messages[ idx ]
notify title, text
flash_messages.length = 0
$.cookie 'flash-messages', '[]'
#...............................................................................................
# after 0.5, -> notify "Attention Y'All", 'the sublime message is talking to you'
# after 1.5, -> notify "Attention Y'All", 'hear hear'
#===================================================================================================
# ( META name: 'description', content: O[ 'description' ] ) if O[ 'description' ]?
# LINK rel: 'stylesheet', href: '/public/cssnormalize-min.css'
# LINK rel: 'stylesheet', href: '/public/mingkwai.css'
# LINK rel: 'stylesheet', href: '/public/font-awesome-4.0.0/css/font-awesome.css'
LINK rel: 'shortcut icon', href: '/public/favicon.ico?v6'
# SCRIPT src: '/public/github_com_carhartl_jquery-cookie/jquery.cookie.js'
# SCRIPT src: '/public/coffeenode-tagtool/main.js'
# SCRIPT src: '/public/mingkwai.js'
COMMENT '#head-bottom'
#.....................................................................................................
BODY ".#{page_style}", =>
COMMENT '#body-top'
#===================================================================================================
# Notifications
#...................................................................................................
# <div id="notify-wrap" style="display: none;"><div id="notify-default"><h1>#{title}</h1><p>#{text}</p></div></div>
DIV '#notify-wrap', style: 'display: none;', =>
DIV '#notify-default', =>
H1 => TEXT '\#{title}'
P => TEXT '\#{text}'
#===================================================================================================
DIV id: 'login-reminder', =>
if logged_in
TEXT "you are logged in as #{request[ 'session' ][ 'user' ]}"
else
null
#...................................................................................................
RAW content
if logged_in
DIV => A href: '/logout', 'log out'
else
unless request[ 'url' ] is '/login'
DIV => A href: '/login', 'log in'
DIV => A href: '/', 'home'
DIV => A href: '/restricted', 'restricted'
DIV => A href: '/welcome', 'welcome'
DIV => A href: '/goodbye', 'goodbye'
DIV => A href: '/notfound', 'not found'
DIV => A href: '/contact', 'contact'
DIV => A href: '/imprint', 'imprint'
DIV => A href: '/privacy', 'privacy'
#...................................................................................................
COMMENT '#body-bottom'
#===========================================================================================================
# GENERAL VIEWS
#-----------------------------------------------------------------------------------------------------------
@homepage = ( request, response, next ) ->
log TRM.blue 'homepage'
O = request[ 'A1' ]
O[ 'title' ] = 'Homepage for Alpha-One'
#.........................................................................................................
return render =>
H1 'Home'
DIV "homepage for alpha-one"
#-----------------------------------------------------------------------------------------------------------
@welcome = ( request, response, next ) ->
log TRM.blue 'welcome'
O = request[ 'A1' ]
O[ 'title' ] = 'Welcome!'
#.........................................................................................................
return render =>
H1 'Welcome'
DIV "welcome to alpha-one"
#-----------------------------------------------------------------------------------------------------------
@goodbye = ( request, response, next ) ->
log TRM.blue 'goodbye'
O = request[ 'A1' ]
O[ 'title' ] = 'Good-Bye'
#.........................................................................................................
return render =>
H1 'Good-Bye'
DIV "good-bye from alpha-one"
#-----------------------------------------------------------------------------------------------------------
@not_found = ( request, response ) ->
log TRM.blue 'not_found'
O = request[ 'A1' ]
O[ 'title' ] = 'Not Found'
A1.HTTP.not_found request, response
#.........................................................................................................
return render =>
H1 '404'
DIV "nothing found for #{request[ 'url' ]}"
#===========================================================================================================
# BOILERPLATE VIEWS
#-----------------------------------------------------------------------------------------------------------
@contact = ( request, response ) ->
log TRM.blue 'contact'
O = request[ 'A1' ]
O[ 'title' ] = 'contact'
#.........................................................................................................
return render =>
H1 'Contact'
DIV "Contact us at info@example.com"
#-----------------------------------------------------------------------------------------------------------
@imprint = ( request, response ) ->
log TRM.blue 'imprint'
O = request[ 'A1' ]
O[ 'title' ] = 'imprint'
request[ 'A1' ][ 'flash' ] 'Welcome...', '...to the mighty Alpha-One Imprint page!'
request[ 'A1' ][ 'flash' ] 'Info', 'We accept pull requests'
#.........................................................................................................
return render =>
H1 'Imprint'
DIV "The maintainers of this site are somewhat responsible for some content."
#-----------------------------------------------------------------------------------------------------------
@privacy = ( request, response ) ->
log TRM.blue 'privacy'
O = request[ 'A1' ]
O[ 'title' ] = 'privacy'
#.........................................................................................................
return render =>
H1 'Privacy'
DIV "Yeah, privacy. Well, we take it seriously."
#===========================================================================================================
# LOGIN / LOGOUT VIEWS
#-----------------------------------------------------------------------------------------------------------
### TAINT these things should probably go into their own module, no? ###
#-----------------------------------------------------------------------------------------------------------
@login_get = ( request, response ) ->
log TRM.blue 'login_get'
O = request[ 'A1' ]
O[ 'title' ] = 'Log In or Sign Up'
comes_from = request[ 'cookies' ]?[ 'comes-from' ]
session = request[ 'session' ]
has_session = session?
#.........................................................................................................
if has_session
login_count = session[ 'login-count' ] += 1
else
login_count = 0
#.........................................................................................................
return render =>
#.......................................................................................................
if comes_from?
request[ 'A1' ][ 'flash' ] 'For your information...', "You must log in to visit #{comes_from}"
#.......................................................................................................
if login_count > 1
DIV "attempt to log in: ##{login_count}"
#.......................................................................................................
H1 'Log In or Sign Up'
DIV =>
FORM '#login-form', method: 'post', action: '/login', =>
FIELDSET =>
LEGEND "Log In"
DIV => TEXT_INPUT
label: "Your email or user name:",
name: 'uid-hint'
autofocus: yes
required: yes
DIV => PASSWORD label: "Your password:"
DIV => SUBMIT label: "submit"
DIV =>
FORM '#signup-form', method: 'post', action: '/signup', =>
FIELDSET =>
LEGEND "Sign Up"
DIV => EMAIL label: "Your email:", autocomplete: 'off'
DIV => TEXT_INPUT
label: "Your user name:",
autocomplete: 'off'
name: 'name'
autofocus: yes
required: yes
DIV => PASSWORD label: "Your password:", autocomplete: 'off'
DIV => CONFIRM_PASSWORD label: "Your password again:", autocomplete: 'off'
DIV => SUBMIT label: "submit"
#-----------------------------------------------------------------------------------------------------------
@login_post = ( request, response, done ) ->
log TRM.blue 'login_post'
uid_hint = request[ 'body' ][ 'uid-hint' ]
password = request[ 'body' ][ 'password' ]
info '©11k', 'query arguments:', request[ 'body' ]
#.........................................................................................................
USERDB.authenticate_user user_db, { name: uid_hint, }, password, ( error, user_known, password_matches ) =>
### TAINT code duplication ###
if error?
if error[ 'message' ] is 'connect ECONNREFUSED'
alert """\nthe CoffeeNode UserDB specified as \n#{rpr user_db}\ncan not be accessed"""
else
alert error
return done new Error error.stack
log '©34e', ( TRM.gold uid_hint ), ( TRM.blue password ), ( TRM.truth user_known ), ( TRM.truth password_matches )
#.......................................................................................................
if user_known and password_matches
TRM.dir '©34e', (require 'express').session
### TAINT what to do if request.session does not exist? ###
request.session.regenerate =>
request.session.user = uid_hint
# log TRM.lime '©15z', response.headerSent # if response.headerSent?
A1.HTTP.back_to request, response, '/welcome'
request.session[ 'just-logged-in' ] = yes
message = "You have been logged in as user #{rpr uid_hint}"
request[ 'A1' ][ 'flash' ] "Welcome", message
done message
#.......................................................................................................
else
log TRM.lime '©15z', response.headerSent # if response.headerSent?
A1.HTTP.redirect request, response, '/login'
message = "Your ID #{rpr uid_hint} or password did not match; please try again."
request[ 'A1' ][ 'flash' ] "Login failed", message
done message
return null
#-----------------------------------------------------------------------------------------------------------
@signup_post = ( request, response, done ) ->
log TRM.blue 'signup_post'
email = request[ 'body' ][ 'email' ]
name = request[ 'body' ][ 'name' ]
password = request[ 'body' ][ 'password' ]
password_r = request[ 'body' ][ 'password-r' ]
#.........................................................................................................
unless password is password_r
message = "your passwords do not match"
A1.HTTP.redirect request, response, '/login'
done message
#.........................................................................................................
### TAINT check for password strength (maybe only on client) ###
### TAINT check for email plausibility ###
### TAINT check for email uniqueness ###
### TAINT think up a UID generation method ###
entry =
'name': name
'uid': "#{email}-#{1 * new Date()}" # placeholder for a better method
'password': password
'email': email
#.........................................................................................................
USERDB.create_user user_db, entry, ( error, result ) ->
### TAINT code duplication ###
if error?
if error[ 'message' ] is 'connect ECONNREFUSED'
alert """\nthe CoffeeNode UserDB specified as \n#{rpr user_db}\ncan not be accessed"""
else
alert error
return done new Error error.stack
#.......................................................................................................
### TAINT what to do if request.session does not exist? ###
request.session.regenerate =>
request.session.user = entry[ 'uid' ]
message = "you have been registered as #{entry[ 'uid' ]}"
request[ 'A1' ][ 'flash' ] "Welcome", message
A1.HTTP.back_to request, response, '/welcome-new-user'
done message
#.........................................................................................................
return null
#-----------------------------------------------------------------------------------------------------------
@logout = ( request, response, done ) ->
log TRM.blue 'restricted'
request.session.destroy =>
A1.HTTP.back_to request, response, '/goodbye'
done "You have been logged out."
#.........................................................................................................
return null
#===========================================================================================================
# RESCTRICTED VIEWS
#-----------------------------------------------------------------------------------------------------------
@restricted = ( request, response ) ->
log TRM.blue 'restricted'
O = request[ 'A1' ]
O[ 'title' ] = 'Restricted Area'
uid = request.session.user
#.........................................................................................................
return render =>
H1 'Restricted Area'
DIV "This is the Restricted Area"