diff --git a/.gitignore b/.gitignore index 40b878d..dbf0821 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -node_modules/ \ No newline at end of file +node_modules/* \ No newline at end of file diff --git a/README.md b/README.md index 8ed8b16..d586554 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,12 @@ Ninja Store =========== -**NOTE**: Requires Express 3.x / Not compatible with Express 4.x +~~**NOTE**: Requires Express 3.x / Not compatible with Express 4.x~~ +**NOTE**: NOW (v0.0.2) compatible with Express 4.x ----------- Ninja Store is a very simple Express.js app for you to hack around and uderstand Express better. It is a good project to start learnig Express because if covers GET and POST requests, the Jade template engine, the Stylus CSS engine, login-logout, and sessions. All of it while being a tiny project. ------------ -My book, **Express Web Application Development** is available now - [http://expressjs-book.com/](http://expressjs-book.com/) - - +To run it use `$ DEBUG=ninja-store:* npm start` or if you installed [nodemon](http://nodemon.io/) (via `npm install -g nodemon`) use `$ nodemon app` \ No newline at end of file diff --git a/app.js b/app.js index 7254810..07c9f8d 100644 --- a/app.js +++ b/app.js @@ -1,43 +1,88 @@ -var express = require('express') - , http = require('http') - , path = require('path'); - -var store = store = require('./routes/store'); -var app = express(); - -app.configure(function(){ - app.set('port', process.env.PORT || 3000); - app.set('views', __dirname + '/views'); - app.set('view engine', 'jade'); - app.use(express.favicon()); - app.use(express.logger('dev')); - app.use(express.bodyParser()); - app.use(express.methodOverride()); - app.use(express.cookieParser('your secret here')); - app.use(express.session()); - app.use(app.router); - app.use(require('stylus').middleware(__dirname + '/public')); - app.use(express.static(path.join(__dirname, 'public'))); -}); - -app.configure('development', function(){ - app.use(express.errorHandler()); -}); - -app.get('/', store.home); -app.post('/', store.home_post_handler); -// display the list of item -app.get('/items', store.items); -// show individual item -app.get('/item/:id', store.item); -// show general pages -app.get('/page', store.page); -app.get('/logout', function(req, res) { - // delete the session variable - delete req.session.username; - // redirect user to homepage - res.redirect('/'); -}); -http.createServer(app).listen(app.get('port'), function(){ - console.log("Express server listening on port " + app.get('port')); -}); +var express = require('express'); +var path = require('path'); +var favicon = require('serve-favicon'); +var logger = require('morgan'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); +var session = require ('express-session'); +var store = require('./routes/store'); + +var app = express(); + +// view engine setup +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'jade'); + +// uncomment after placing your favicon in /public +//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); +app.use(logger('dev')); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieParser()); +app.use(require('stylus').middleware(path.join(__dirname, 'public'))); +app.use(express.static(path.join(__dirname, 'public'))); +/* + Failure to set "saveUninitialized" and "resave" will generate two warnings: + express-session deprecated undefined resave option; provide resave option app.js:7:9 + express-session deprecated undefined saveUninitialized option; provide saveUninitialized option app.js:7:9 + This is simply saying the default values will change so they want to ensure that by setting the values + explicitly now. By doing this you won't run into unexpected behavior if the defaults do change in the + near future. +*/ +app.use(session({ + secret: 'qwertzuiopasdfghjklyxcvbnm', + cookie: { maxAge: 100000 }, + saveUninitialized: true, // (default: true) + resave: true, // (default: true) + })); + +// GET and POST for root Page +app.get('/', store.home); +app.post('/', store.home_post_handler); +// display the list of item +app.get('/items', store.items); +// show individual item +app.get('/item/:id', store.item); +// show general pages +app.get('/page', store.page); +// logout link +app.get('/logout', function(req, res) { + // delete the session variable + delete req.session.username; + // redirect user to homepage + res.redirect('/'); +}); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new Error('Not Found'); + err.status = 404; + next(err); +}); + +// error handlers + +// development error handler +// will print stacktrace +if (app.get('env') === 'development') { + app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: err + }); + }); +} + +// production error handler +// no stacktraces leaked to user +app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: {} + }); +}); + + +module.exports = app; diff --git a/bin/www b/bin/www new file mode 100644 index 0000000..efa5253 --- /dev/null +++ b/bin/www @@ -0,0 +1,90 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var app = require('../app'); +var debug = require('debug')('ninja-store:server'); +var http = require('http'); + +/** + * Get port from environment and store in Express. + */ + +var port = normalizePort(process.env.PORT || '4000'); +app.set('port', port); + +/** + * Create HTTP server. + */ + +var server = http.createServer(app); + +/** + * Listen on provided port, on all network interfaces. + */ + +server.listen(port); +server.on('error', onError); +server.on('listening', onListening); + +/** + * Normalize a port into a number, string, or false. + */ + +function normalizePort(val) { + var port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "error" event. + */ + +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + var bind = typeof port === 'string' + ? 'Pipe ' + port + : 'Port ' + port; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error(bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +} + +/** + * Event listener for HTTP server "listening" event. + */ + +function onListening() { + var addr = server.address(); + var bind = typeof addr === 'string' + ? 'pipe ' + addr + : 'port ' + addr.port; + debug('Listening on ' + bind); +} diff --git a/package.json b/package.json index 395eedf..789ed4e 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,19 @@ -{ - "name": "application-name", - "version": "0.0.1", - "private": true, - "scripts": { - "start": "node app" - }, - "dependencies": { - "express": "3.1.0", - "jade": "*", - "stylus": "*" - } +{ + "name": "ninja-store", + "version": "0.0.2", + "private": true, + "scripts": { + "start": "node ./bin/www" + }, + "dependencies": { + "body-parser": "~1.13.2", + "cookie-parser": "~1.3.5", + "debug": "~2.2.0", + "express": "~4.13.1", + "express-session": "^1.13.0", + "jade": "~1.11.0", + "morgan": "~1.6.1", + "serve-favicon": "~2.3.0", + "stylus": "0.42.3" + } } diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index a8f22d7..4268017 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -1,18 +1,15 @@ body { - padding: 50px; + padding: 0; font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; - background: #ccc; } a { color: #0069ff; } -#container { +#wrapper { width: 450px; margin: 0 auto; padding: 40px 20px; background: #fff; - box-shadow: 1px 3px 3px #333; - border-radius: 5px; } #logo { text-align: center; diff --git a/public/stylesheets/style.styl b/public/stylesheets/style.styl index 3d5405d..1dfc529 100644 --- a/public/stylesheets/style.styl +++ b/public/stylesheets/style.styl @@ -1,4 +1,4 @@ -body +body padding: 0 font: 14px "Lucida Grande", Helvetica, Arial, sans-serif @@ -18,4 +18,4 @@ a margin: 20px 0 50px #userbar - margin-bottom: 10px + margin-bottom: 10px \ No newline at end of file diff --git a/routes/store.js b/routes/store.js index c9b03ff..a4084c9 100644 --- a/routes/store.js +++ b/routes/store.js @@ -1,54 +1,54 @@ -// our 'database' -var items = { - SKN:{name:'Shuriken', price:100}, - ASK:{name:'Ashiko', price:690}, - CGI:{name:'Chigiriki', price:250}, - NGT:{name:'Naginata', price:900}, - KTN:{name:'Katana', price:1000} -}; - -// handler for homepage -exports.home = function(req, res) { - // if user is not logged in, ask them to login - if (typeof req.session.username == 'undefined') res.render('home', { title: 'Ninja Store'}); - // if user is logged in already, take them straight to the items list - else res.redirect('/items'); -}; - -// handler for form submitted from homepage -exports.home_post_handler = function(req, res) { - // if the username is not submitted, give it a default of "Anonymous" - username = req.body.username || 'Anonymous'; - // store the username as a session variable - req.session.username = username; - // redirect the user to homepage - res.redirect('/'); -}; - -// handler for displaying the items -exports.items = function(req, res) { - // don't let nameless people view the items, redirect them back to the homepage - if (typeof req.session.username == 'undefined') res.redirect('/'); - else res.render('items', { title: 'Ninja Store - Items', username: req.session.username, items:items }); -}; - -// handler for displaying individual items -exports.item = function(req, res) { - // don't let nameless people view the items, redirect them back to the homepage - if (typeof req.session.username == 'undefined') res.redirect('/'); - else { - var name = items[req.params.id].name; - var price = items[req.params.id].price; - res.render('item', { title: 'Ninja Store - ' + name, username: req.session.username, name:name, price:price }); - } -}; - -// handler for showing simple pages -exports.page = function(req, res) { - var name = req.query.name; - var contents = { - about: 'Ninja Store sells the coolest ninja stuff in the world. Anyone shopping here is cool.', - contact: 'You can contact us at
Ninja Store,