WebFund 2014W: Assignment 2: Difference between revisions
Line 270: | Line 270: | ||
exports.connectToDBs = connectToDBs; | exports.connectToDBs = connectToDBs; | ||
</source> | </source> | ||
===views/layout.jade=== | |||
<source line lang="html"> | |||
doctype html | |||
html | |||
head | |||
title= title | |||
script(src='/vendor/jquery/jquery.js') | |||
script('vendor/bootstrap/dist/js/bootstrap.js') | |||
link(rel='stylesheet', href='/vendor/bootstrap/dist/css/bootstrap.css') | |||
link(rel='stylesheet', href='/stylesheets/style.css') | |||
block header | |||
body | |||
block content | |||
</source> | |||
===views/index.jade=== | |||
<source line lang="html"> | |||
extends layout | |||
block header | |||
script(src='/javascripts/home.js') | |||
block content | |||
h1= title | |||
- if(error) | |||
div.alert-error #{error} | |||
p Please log in | |||
div | |||
form(action="/start", method="post") | |||
div.control-group.input-append | |||
input(type="text", name="playername") | |||
label.add-on(for="playername") Player Name | |||
div.control-group.input-append | |||
input(type="password", name="password") | |||
label.add-on(for="password") Password | |||
button(type="submit") Start | |||
button#register(type="button") Register | |||
</source> | |||
===views/registered.jade=== | |||
<source line lang="html"> | |||
extends layout | |||
block content | |||
h1 Registration Successful | |||
p You have successfully registered player #{playername} | |||
form(action="/", method="get") | |||
button(type="submit") Return | |||
</source> | |||
===views/room.jade=== | |||
<source line lang="html"> | |||
extends layout | |||
block content | |||
h1= title | |||
p #{description} | |||
p Go to: | |||
ul | |||
each theExit in roomExits | |||
li | |||
a(href= theExit) #{theExit} | |||
form(action="/quit", method="post") | |||
button(type="submit") Quit | |||
</source> | |||
===storeRooms.js=== | ===storeRooms.js=== |
Revision as of 16:00, 5 February 2014
This assignment is not yet finalized
In this assignment you are to examine, modify, and answer questions regarding the adventure-demo sample node application. The files of this application are also listed below. Note that to run this application you'll first need to run node storeRooms.js to setup the rooms collection in MongoDB.
Please submit your answers as a single zip file called "<username>-comp2406-assign2.zip" (where username is your MyCarletonOne username). This zip file should unpack into a directory of the same name (minus the .zip extension of course). This directory should contain:
- your modified version of adventure-demo (one version with all code changes) and
- a text file called "answers.txt" or "answers.pdf" at the top level (in text or PDF format, respectively) that contains the answers to the all of the questions, with the first four lines being "COMP 2406 Assignment 2", your name, student number, and the date. For questions requiring code changes (Part B), explain in your answers file which parts of the program were changed. You may wish to format answers.txt in Markdown to improve its appearance.
No other formats will be accepted. Submitting in another format will likely result in your assignment not being graded and you receiving no marks for this assignment. In particular do not submit an MS Word or OpenOffice file as your answers document!
Questions
There are ?? points below in three parts. Answer all questions.
Part A
What happens when you change the following lines in adventure-demo? Describe the change in behaviour as if each deletion is the only change made to the application. If a runtime error is generated please describe the error and briefly explain why that error was produced. (two points each, ?? total)
Part B
Describe how to change the code to add the following features/change the following behaviour. If the changes are small, specify the line(s) to changed. If the changes are more substantial, you may just list the entire modified file. These changes are cumulative, so the changes for the third question should take into account those made previously.
- How can you change this app to list all of the currently logged in users on /users?
Part C
- (5 points) What is the difference between the Login and Register button on the initial screen? Do they work the same way?
- (4 points) MongoDB's "tables" are collections; they are grouped together into databases. What MongoDB database is used by this application? What collections?
- (2 points) How long before this app's session cookies expire? How do you know?
- (4 points) Do sessions and user accounts persist across web application restarts? Why or why not?
- (4 points) In the POST function for /start, it processes a username and password supplied by the user. What object stores this information in node for our program to access? What line in app.js loads the code to provides the values for this object?
- (4 points) Why are there three arguments to the app.get()'s, rather than the previous two? What does the third argument do?
- The routes.register() has multiple nested functions. What do each of them do, and why are they nested the way they are?
Source
app.js
/**
* Module dependencies.
*/
var express = require('express');
var routes = require('./routes');
var path = require('path');
var fs = require('fs');
var http = require('https'); // note https, not http!
var MongoStore = require('connect-mongo')(express);
var app = express();
var options = {
key: fs.readFileSync('keys/comp2406-private-key.pem'),
cert: fs.readFileSync('keys/comp2406-cert.pem')
};
// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(express.cookieParser('COMP 2406 adventure demo!'));
app.use(express.session({
cookie: {maxAge: 60000 * 20} // 20 minutes
, secret: "Shh... I'm a secret"
, store: new MongoStore({db: "adventure-demo"})
}));
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
// set NODE_ENV environment variable to change from development mode
if ('development' == app.get('env')) {
app.use(express.errorHandler());
}
app.get('/', routes.redirectLoggedIn, routes.index);
app.post("/register", routes.register);
app.post("/start", routes.start);
app.post("/quit", routes.quit);
var createRooms = function() {
var i, theRoom;
routes.getRooms().toArray(
function(err, docs) {
if (err) {
throw "Couldn't find active room list";
}
var activeRooms = docs[0].activeRooms;
activeRooms.forEach(function(roomName) {
console.log('Creating room: ' + roomName);
app.get('/' + roomName,
routes.makeRoomHandler(roomName));
});
}
);
}
routes.connectToDBs(createRooms);
http.createServer(options, app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port') +
' in ' + app.get('env') + ' mode.');
});
routes/index.js
var bcrypt = require("bcrypt");
var mc = require('mongodb').MongoClient;
var playersCollection;
var roomsCollection;
var connectToDBs = function(callback) {
mc.connect('mongodb://localhost/adventure-demo', function(err, db) {
if (err) {
throw err;
}
playersCollection = db.collection('players');
roomsCollection = db.collection('rooms');
callback();
});
}
var index = function(req, res){
res.render('index', { title: 'COMP 2406 Adventure Demo',
error: req.query.error });
}
var redirectLoggedIn = function(req, res, next) {
if (req.session.player) {
res.redirect("/" + req.session.player.room);
} else {
next();
}
}
var redirectNotLoggedIn = function(req, res, next) {
if (req.session.player) {
next();
} else {
res.redirect("/");
}
}
var register = function(req, res) {
var playername = req.body.playername;
var password = req.body.password;
var addPlayer = function(err, players) {
if(players.length!=0){
res.redirect("/?error=player already exists");
return;
}
//generate a salt, with 10 rounds (2^10 iterations)
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(password, salt, function(err, hash) {
var newPlayer = {
playername: playername,
password: hash,
room: "bridge"
};
playersCollection.insert(newPlayer, function(err, newPlayers){
if (err) {
throw err;
} else {
res.render('registered',
{ playername: newPlayers[0].playername });
}
});
});
});
};
playersCollection.find({playername: playername}).toArray(addPlayer);
}
var start = function(req, res){
var playername = req.body.playername;
var password = req.body.password;
playersCollection.findOne({playername: playername}, function(err, player){
if (err || !player){
req.session.destroy(function(err) {
res.redirect("/?error=invalid playername or password");
});
return;
}
bcrypt.compare(password, player.password, function(err, authenticated){
if(authenticated){
req.session.player = player;
delete req.session.player._id;
res.redirect("/" + player.room);
} else {
req.session.destroy(function(err) {
res.redirect("/?error=invalid playername or password");
});
}
});
});
}
var quit = function(req, res){
req.session.destroy(function(err){
if(err){
console.log("Error: %s", err);
}
res.redirect("/");
});
}
var makeRoomHandler = function(roomName) {
handler = function(req, res) {
if (req.session.player) {
var player = req.session.player;
player.room = roomName;
playersCollection.update({"playername": player.playername},
player, function(err, count) {
if (err) {
console.log(
"Couldn't save player state");
}
});
roomsCollection.findOne(
{name: roomName},
function(err, room) {
if (err || !room) {
throw "Couldn't find room " + roomName;
}
res.render("room.jade", room);
}
);
} else {
res.redirect("/");
}
}
return handler;
}
var getRooms = function() {
return roomsCollection.find({name: "roomList"});
}
exports.index = index;
exports.redirectLoggedIn = redirectLoggedIn;
exports.redirectNotLoggedIn = redirectNotLoggedIn;
exports.register = register;
exports.start = start;
exports.quit = quit;
exports.makeRoomHandler = makeRoomHandler;
exports.getRooms = getRooms;
exports.connectToDBs = connectToDBs;
views/layout.jade
doctype html
html
head
title= title
script(src='/vendor/jquery/jquery.js')
script('vendor/bootstrap/dist/js/bootstrap.js')
link(rel='stylesheet', href='/vendor/bootstrap/dist/css/bootstrap.css')
link(rel='stylesheet', href='/stylesheets/style.css')
block header
body
block content
views/index.jade
extends layout
block header
script(src='/javascripts/home.js')
block content
h1= title
- if(error)
div.alert-error #{error}
p Please log in
div
form(action="/start", method="post")
div.control-group.input-append
input(type="text", name="playername")
label.add-on(for="playername") Player Name
div.control-group.input-append
input(type="password", name="password")
label.add-on(for="password") Password
button(type="submit") Start
button#register(type="button") Register
views/registered.jade
extends layout
block content
h1 Registration Successful
p You have successfully registered player #{playername}
form(action="/", method="get")
button(type="submit") Return
views/room.jade
extends layout
block content
h1= title
p #{description}
p Go to:
ul
each theExit in roomExits
li
a(href= theExit) #{theExit}
form(action="/quit", method="post")
button(type="submit") Quit
storeRooms.js
// storeRooms.js
var mc = require('mongodb').MongoClient;
var rooms = [
{
name: "roomList",
activeRooms: ['bridge', 'sickbay', 'engineering']
},
{ name: "bridge",
title: "Bridge",
description: "You are on the Bridge. There are big comfy chairs and a big screen here.",
roomExits: ['sickbay'],
},
{
name: "engineering",
title: "Engineering",
description: "You are in Engineering. There are lots of funny instruments, many smaller screens, and kind of uncomfortable chairs.",
roomExits: ['sickbay'],
},
{
name: "sickbay",
title: "Sickbay",
description: "You are in Sickbay. It is in the center of the ship, the safest place there is. There are lots of comfy beds here and blinky lights.",
roomExits: ['engineering','bridge'],
},
{
name: "secret",
title: "Secret Room",
description: "This is a secret room. How did you get here?",
roomExits: ['engineering', 'sickbay', 'bridge'],
},
];
mc.connect('mongodb://localhost/adventure-demo', function(err, db) {
if (err) {
throw err;
}
var roomsCollection = db.collection('rooms');
roomsCollection.drop(function(err, count) {
if (err) {
console.log("No collection to drop.");
} else {
console.log("room collection dropped.");
}
roomsCollection.insert(rooms, function(err, rooms) {
if (err) {
throw err;
}
rooms.forEach(function(room) {
console.log("Added " + room.name);
});
db.close();
});
});
});