'",
- "4) You will need to set enable the trust proxy option for express in order to access the ip from the request object. Dont' bother too much with it, the default options will do for our case.",
- "5) On request error the route should responde with status 500 and the error in the body",
- "6) Othewise it should responde with the status code and body returned by the remote server"
+ "One of the greatest features of using a template engine is being able to pass variables from the server to the template file before rendering it to HTML.",
+ "In your Pug file, you're about to use a variable by referencing the variable name as #{variable_name}
inline with other text on an element or by using an equal side on the element without a space such as p= variable_name
which sets that p elements text to equal the variable.",
+ "We strongly recomend looking at the syntax and structure of Pug here on their Githubs README. Pug is all about using whitespace and tabs to show nested elements and cutting down on the amount of code needed to make a beautiful site.",
+ "Looking at our pug file 'index.pug' included in your project, we used the variables title and message",
+ "To pass those alone from our server, you will need to add an object as a second argument to your res.render with the variables and their value. For example, pass this object along setting the variables for your index view: {title: 'Hello', message: 'Please login'
",
+ "It should look like: res.render(process.cwd() + '/views/pug/index', {title: 'Hello', message: 'Please login'});
",
+ "Now refresh your page and you should see those values rendered in your view in the correct spot as layed out in your index.pug file! Submit your page when you think you've got it right."
],
"challengeSeed": [],
"tests": [
{
- "text": "",
- "testString": ""
+ "text": "Pug render variables correct",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/') .then(data => { assert.match(data, /pug-variable(\"|')>Please login/gi, 'Your projects home page should now be rendered by pug with the projects .pug file unaltered'); }, xhr => { throw new Error(xhr.statusText); })"
}
],
"solutions": [],
- "hints": [
- "request readme: https://github.com/request/request",
- "express enable: http://expressjs.com/en/api.html#app.enable"
- ],
- "type": "waypoint",
+ "hints": [],
+ "type": "backend",
"challengeType": 0,
"translations": {}
},
{
- "id": "587d7fbe367417b2b2512c2b",
- "title": "Get Geodata from Cookies",
+ "id": "5895f70cf9fc0f352b528e65",
+ "title": "Setting up Passport",
"description": [
- "Right now the client requests for the geolocation data by their ip and sends it to the server every time they are creating a message.",
- "That makes sense, but it would be better to cache the geolocation data so that we do not request all the time the external api. Geolocation data do not change that often after all.",
- "This can be done by setting a cookie on the response header. In express to do that we could use the res.cookie utility.",
- "Instructions:",
- "1) update route GET '/api/geo' so that it will set a cookie before responding with json. You may use res.cookie to do the job.",
- "2) include package cookie-parser on package.json",
- "3) update route POST '/api/message' so that it will create the message withe the geolocation taken from the cookie instead of the body of the request ctrl-f '#challenge' in this file to fill in the missing code to complete this challenge"
+ "It's time to set up Passport so we can finally start allowing a user to register or login to an account! In addition to Passport, we will use Express-session to handle sessions. Using this middleware saves the session id as a cookie in the client and allows us to access the session data using that id on the server. This way we keep personal account information out of the cookie used by the client to verify to our server they are authenticated and just keep the key to access the data stored on the server.",
+ "To set up Passport for use in your project, you will need to add it as a dependancy first in your package.json. \"passport\": \"^0.3.2\"
",
+ "In addition, add Express-session as a dependancy now as well. Express-session has a ton of advanced features you can use but for now we're just going to use the basics! \"express-session\": \"^1.15.0\"
",
+ "You will need to set up the session settings now and initilize Passport. Be sure to first create the variables 'session' and 'passport' to require 'express-session' and 'passport' respectively.",
+ "To set up your express app to use use the session we'll define just a few basic options. Be sure to add 'SESSION_SECRET' to your .env file and give it a random value. This is used to compute the hash used to encrypt your cookie!",
+ "app.use(session({\n secret: process.env.SESSION_SECRET,\n resave: true,\n saveUninitialized: true,\n}));
",
+ "As well you can go ahead and tell your express app to use 'passport.initialize()' and 'passport.session()'. (For example, app.use(passport.initialize());
)",
+ "Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point here."
],
"challengeSeed": [],
"tests": [
{
- "text": "",
- "testString": ""
+ "text": "Passort and Express-session are dependencies",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/_api/package.json') .then(data => { var packJson = JSON.parse(data); assert.property(packJson.dependencies, 'passport', 'Your project should list \"passport\" as a dependency'); assert.property(packJson.dependencies, 'express-session', 'Your project should list \"express-session\" as a dependency'); }, xhr => { throw new Error(xhr.statusText); })"
+ },
+ {
+ "text": "Dependencies correctly required",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /require.*(\"|')passport(\"|')/gi, 'You should have required passport'); assert.match(data, /require.*(\"|')express-session(\"|')/gi, 'You should have required express-session'); }, xhr => { throw new Error(xhr.statusText); })"
+ },
+ {
+ "text": "Express app uses new dependencies",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /passport.initialize/gi, 'Your express app should use \"passport.initialize()\"'); assert.match(data, /passport.session/gi, 'Your express app should use \"passport.session()\"'); }, xhr => { throw new Error(xhr.statusText); })"
+ },
+ {
+ "text": "Session and session secret correctly set up",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /secret:( |)process.env.SESSION_SECRET/gi, 'Your express app should have express-session set up with your secret as process.env.SESSION_SECRET'); }, xhr => { throw new Error(xhr.statusText); })"
}
],
"solutions": [],
- "hints": [
- "express res.cookie: http://expressjs.com/en/api.html#res.cookie",
- "express cookie parser: https://github.com/expressjs/cookie-parser"
- ],
- "type": "waypoint",
+ "hints": [],
+ "type": "backend",
"challengeType": 0,
"translations": {}
},
{
- "id": "587d8246367417b2b2512c34",
- "title": "Access the Default Avatar File Route",
+ "id": "5895f70cf9fc0f352b528e66",
+ "title": "Serialization of a User Object",
"description": [
- "Did you notice that broken avatar in the app?",
- "Don't you think its about time to fixed it?",
- "We have set the default url of the user's avatar to be be at /avatar/default.",
- "That means that you will have to create a route that will return the file data.",
- "Until now we used routes to return json and htmls. However in this case we are going",
- "to pipe a read stream into the response in order to respond with the file data.",
- "Use following line to get the path of the path of the avatar:",
- "var avatarPath = path.join(__dirname, 'node_modules/fcc-advanced-express/img/defaultAvatar.png');",
- "Instructions:",
- "1) Create route GET '/avatar/default'",
- "2) Use node's native file system library to get a read stream of the avatar file",
- "3) pipe the read stream into the response. You may hit the route on your browser to check if u see an image.",
- "Note: remember that piping file data to the response works with any type of file. All files contain data that if piped to the response can be sent to the browser. Try to pipe a js script and you will be able to see if on your browser if you hit the route :)"
+ "Serialization and deserialization are important concept in reguards to authentication. To serialize an object means to convert its contents into a small key essentially that can then be deserialized into the original object. This is what allows us to know whos communicated with the server without having to send the authentication data like username and password at each request for a new page.",
+ "To set this up properly, we need to have a serialize function and a deserialize function. In passport we create these with passport.serializeUser( OURFUNCTION )
and passport.dederializeUser( OURFUNCTION )
",
+ "The serializeUser is called with 2 arguments, the full user object and a callback used by passport. Returned in the callback should be a unique key to identify that user- the easiest one to use being the users _id in the object as it should be unique as it generated by MongoDb. Similarly deserializeUser is called with that key and a callback function for passport as well, but this time we have to take that key and return the users full object to the callback. To make a query search for a Mongo _id you will have to create const ObjectID = require('mongodb').ObjectID;
, and then to use it you call new ObjectID(THE_ID)
. Be sure to add MongoDB as a dependency. You can see this in the examples below:",
+ "passport.serializeUser((user, done) => {\n done(null, user._id);\n });
passport.deserializeUser((id, done) => {\n db.collection('users').findOne(\n {_id: new ObjectID(id)},\n (err, doc) => {\n done(null, doc);\n }\n );\n });
",
+ "NOTE: This deserializeUser will throw an error until we set up the DB in the next step so comment out the whole block and just call done(null, null)
in the function deserializeUser.",
+ "Submit your page when you think you've got it right."
],
"challengeSeed": [],
"tests": [
{
- "text": "",
- "testString": ""
+ "text": "Serialize user function correct",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /passport.serializeUser/gi, 'You should have created your passport.serializeUser function'); assert.match(data, /null, user._id/gi, 'There should be a callback in your serializeUser with (null, user._id)'); }, xhr => { throw new Error(xhr.statusText); })"
+ },
+ {
+ "text": "Deserialize user function corrent",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /passport.deserializeUser/gi, 'You should have created your passport.deserializeUser function'); assert.match(data, /null,( |)null/gi, 'There should be a callback in your deserializeUser with (null, null) for now'); }, xhr => { throw new Error(xhr.statusText); })"
+ },
+ {
+ "text": "MongoDB is a dependency",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/_api/package.json') .then(data => { var packJson = JSON.parse(data); assert.property(packJson.dependencies, 'mongodb', 'Your project should list \"mongodb\" as a dependency'); }, xhr => { throw new Error(xhr.statusText); })"
+ },
+ {
+ "text": "Mongodb properly required including the ObjectId",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /require.*(\"|')mongodb(\"|')/gi, 'You should have required mongodb'); assert.match(data, /new ObjectID.*id/gi, 'Even though the block is commented out, you should use new ObjectID(id) for when we add the database'); }, xhr => { throw new Error(xhr.statusText); })"
}
],
"solutions": [],
- "hints": [
- "express res.cookie: http://expressjs.com/en/api.html#res.cookie",
- "express cookie parser: https://github.com/expressjs/cookie-parser"
- ],
- "type": "waypoint",
+ "hints": [],
+ "type": "backend",
"challengeType": 0,
"translations": {}
- }
+ },
+ {
+ "id": "5895f70cf9fc0f352b528e67",
+ "title": "Implement the Serialization of a Passport User",
+ "description": [
+ "Right now we're not loading an actually users object since we havn't set up our database. This can be done many different ways, but for our project we will connect to the database once when we start the server and keep a persistant connection for the full life-cycle of the app.",
+ "To do this, add MongoDB as a depenacy and require it in your server. (const mongo = require('mongodb').MongoClient;
)",
+ "Now we want to the connect to our database then start listening for requests. The purpose of this is to not allow requests before our database is connected or if there is a database error. To accomplish you will want to encompass your serialization and your app listener in the following:",
+ "mongo.connect(process.env.DATABASE, (err, db) => {\n if(err) {\n console.log('Database error: ' + err);\n } else {\n console.log('Successful database connection');\n\n //serialization and app.listen\n\n}});
",
+ "You can now uncomment the block in deserializeUser and remove your done(null, null)
. Be sure to set DATABASE in your .env file to your database's connection string (for example: DATABASE=mongodb://admin:pass@mlab.com:12345/my-project
). You can set up a free database on mLab. Congratulations- you've finished setting up serialization!",
+ "Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point here."
+ ],
+ "challengeSeed": [],
+ "tests": [
+ {
+ "text": "Database connection is present",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /mongo.connect/gi, 'You should have created a connection to your database'); assert.match(data, /mongo.connect[^]*app.listen[^]*}[^]*}/gi, 'You should have your app.listen nested at within your database connection at the bottom'); }, xhr => { throw new Error(xhr.statusText); })"
+ },
+ {
+ "text": "Deserialization is now correctly using the DB and done(null, null)
is erased",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.notMatch(data, /null,( |)null/gi, 'The callback in deserializeUser of (null, null) should be completely removed for the db block uncommented out'); }, xhr => { throw new Error(xhr.statusText); })"
+ }
+ ],
+ "solutions": [],
+ "hints": [],
+ "type": "backend",
+ "challengeType": 0,
+ "translations": {}
+ },
+ {
+ "id": "5895f70df9fc0f352b528e68",
+ "title": "Authentication Strategies",
+ "description": [
+ "A strategy is a a way of authenticating a user. You can use a strategy for allowing users to authenticate based on localally saved infomation(if you have them register first) or from a variety of providers such as Google or Github. For this project we will set up a local strategy. To see a list of the 100's of strategies, visit Passports site here.",
+ "Add passport-local as a dependancy and add it to your server as follows: const LocalStrategy = require('passport-local');
",
+ "Now you will have to tell passport to use an instantiated LocalStartegy object with a few settings defined. Make sure this as well as everything from this point on is encapuled in the database connection since it relys on it! passport.use(new LocalStrategy(\n function(username, password, done) {\n db.collection('users').findOne({ username: username }, function (err, user) {\n console.log('User '+ username +' attempted to log in.');\n if (err) { return done(err); }\n if (!user) { return done(null, false); }\n if (password !== user.password) { return done(null, false); }\n return done(null, user);\n });\n }\n));
This is defining the process to take when we try to authenticate someone locally. First it trys to find a user in our database with the username entered, then it checks for the password to match, then finally if no errors have popped up that we checked for, like an incorrect password, the users object is returned and they are authenticated.",
+ "Many strategies are set up using different settings, general it is easy to set it up based on the README in that strategies repository though. A good example of this is the Github strategy where we don't need to worry about a username or password because the user will be sent to Github's auth page to authenticate and as long as they are logged in and agree then Github returns their profile for us to use.",
+ "In the next step we will set up how to actually call the authentication strategy to validate a user based on form data! Submit your page when you think you've got it right up to this point."
+ ],
+ "challengeSeed": [],
+ "tests": [
+ {
+ "text": "Passport-local is a dependency",
+ "testString": " getUserInput => $.get(getUserInput('url')+ '/_api/package.json') .then(data => { var packJson = JSON.parse(data); assert.property(packJson.dependencies, 'passport-local', 'Your project should list \"passport-local \" as a dependency'); }, xhr => { throw new Error(xhr.statusText); })"
+ },
+ {
+ "text": "Passport-local correctly required and setup",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /require.*(\"|')passport-local(\"|')/gi, 'You should have required passport-local'); assert.match(data, /new LocalStrategy/gi, 'You should have told passport to use a new strategy'); assert.match(data, /findOne/gi, 'Your new local strategy should use the findOne query to find a username based on the inputs'); }, xhr => { throw new Error(xhr.statusText); })"
+ }
+ ],
+ "solutions": [],
+ "hints": [],
+ "type": "backend",
+ "challengeType": 0,
+ "translations": {}
+ },
+ {
+ "id": "5895f70df9fc0f352b528e69",
+ "title": "Using Passport Strategies",
+ "description": [
+ "In the index.pug file supplied theres actually a login form. It has previously been hidden because of the inline javascript if showLogin
with the form indented after it. Before showLogin as a variable was never defined, it never rendered the code block containing the form. Go ahead and on the res.render for that page add a new variable to the object showLogin: true
. When you refresh your page, you should then see the form! This form is set up to POST on /login so this is where we should set up to accept the POST and authenticate the user.",
+ "For this challenge you should add the route /login to accept a POST request. To authenticate on this route you need to add a middleware to do so before then sending a response. This is done by just passing another argument with the middleware before your function(req,res)
with your response! The middleware to use is passport.authenticate('local')
.",
+ "passport.authenticate can also take some options as an argument such as: { failureRedirect: '/' }
which is incredibly useful so be sure to add that in as well. As a response after useing the middleware (which will only be called if the authentication middleware passes) should be to redirect the user to /profile and that route should render the view 'profile.pug'.",
+ "If the authentication was successful, the user object will be saved in req.user.",
+ "Now at this point if you enter a username and password in the form, it should redirect to the home page / and in the console of your server should be 'User {USERNAME} attempted to log in.' since we currently cannot login a user who isn't registered.",
+ "Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point here."
+ ],
+ "challengeSeed": [],
+ "tests": [
+ {
+ "text": "All steps correctly impl in the server.js",
+ "testString": " getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /showLogin:( |)true/gi, 'You should be passing the variable \"showLogin\" as true to your render function for the homepage'); assert.match(data, /failureRedirect:( |)('|\")\\/('|\")/gi, 'Your code should include a failureRedirect to the \"/\" route'); assert.match(data, /login[^]*post[^]*local/gi, 'You should have a route for login which accepts a POST and passport.authenticates local'); }, xhr => { throw new Error(xhr.statusText); })"
+ },
+ {
+ "text": "A POST request to /login correctly redirects to /",
+ "testString": "getUserInput => $.post(getUserInput('url')+ '/login') .then(data => { assert.match(data, /Home page/gi, 'A login attempt at this point should redirect to the homepage since we do not have any registered users'); }, xhr => { throw new Error(xhr.statusText); })"
+ }
+ ],
+ "solutions": [],
+ "hints": [],
+ "type": "backend",
+ "challengeType": 0,
+ "translations": {}
+ },
+ {
+ "id": "5895f70df9fc0f352b528e6a",
+ "title": "Creating New Middleware",
+ "description": [
+ "As in, any user can just go to /profile whether they authenticated or not by typeing in the url. We want to prevent this by checking if the user is authenticated first before rendering the profile page. This is the perfect example of when to create a middleware.",
+ "The challenge here is creating the middleware function ensureAuthenticated(req, res, next)
, which will check if a user is authenticated by calling passports isAuthenticated on the request which in turn checks for req.user is to be defined. If it is then next() should be called, otherwise we can just respond to the request with a redirect to our homepage to login. An implimentation of this middle ware is:",
+ "function ensureAuthenticated(req, res, next) {\n if (req.isAuthenticated()) {\n return next();\n }\n res.redirect('/');\n};
",
+ "Now add ensureAuthenticated as a middleware to the request for the profile page before the argument to the get request containing the function that renders the page.",
+ "app.route('/profile')\n .get(ensureAuthenticated, (req,res) => {\n res.render(process.cwd() + '/views/pug/profile');\n });
",
+ "Submit your page when you think you've got it right."
+ ],
+ "challengeSeed": [],
+ "tests": [
+ {
+ "text": "Middleware ensureAuthenticated should be impl and on our /profile route",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /ensureAuthenticated[^]*req.isAuthenticated/gi, 'Your ensureAuthenticated middleware should be defined and utilize the req.isAuthenticated function'); assert.match(data, /profile[^]*get[^]*ensureAuthenticated/gi, 'Your ensureAuthenticated middleware should be attached to the /profile route'); }, xhr => { throw new Error(xhr.statusText); })"
+ },
+ {
+ "text": "A Get request to /profile correctly redirects to / since we are not authenticated",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/profile') .then(data => { assert.match(data, /Home page/gi, 'An attempt to go to the profile at this point should redirect to the homepage since we are not logged in'); }, xhr => { throw new Error(xhr.statusText); })"
+ }
+ ],
+ "solutions": [],
+ "hints": [],
+ "type": "backend",
+ "challengeType": 0,
+ "translations": {}
+ },
+ {
+ "id": "5895f70ef9fc0f352b528e6b",
+ "title": "Putting a Profile Together",
+ "description": [
+ "Now that we can ensure the user accessing the /profile is authenticated, we can use the infomation contained in 'req.user' on our page!",
+ "Go ahead and pass the object containing the variable username equaling 'req.user.username' into the render method of the profile view. Then go to youre 'profile.pug' view and add the line h2.center#welcome Welcome, #{username}!
creating the h2 element with the class 'center' and id 'welcome' containing the text 'Welcome, ' and the username!",
+ "Also in the profile, add a link to /logout. That route will host the logic to unauthenticate a user. a(href='/logout') Logout
",
+ "Submit your page when you think you've got it right."
+ ],
+ "challengeSeed": [],
+ "tests": [
+ {
+ "text": "Correctly added a Pug render variable to /profile",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /\\/views\\/pug\\/profile[^]*username:( |)req.user.username/gi, 'You should be passing the variable username with req.user.username into the render function of the profile page'); }, xhr => { throw new Error(xhr.statusText); })"
+ }
+ ],
+ "solutions": [],
+ "hints": [],
+ "type": "backend",
+ "challengeType": 0,
+ "translations": {}
+ },
+ {
+ "id": "58965611f9fc0f352b528e6c",
+ "title": "Logging a User Out",
+ "description": [
+ "Creating the logout logic is easy. The route should just unauthenticate the user and redirect to the home page instead of rendering any view.",
+ "In passport, unauthenticating a user is as easy as just calling req.logout();
before redirecting.",
+ "app.route('/logout')\n .get((req, res) => {\n req.logout();\n res.redirect('/');\n });
",
+ "You may have noticed we also we're not handling missing pages (404), the common way to handle this in Node is with the following middleware. Go ahead and add this in after all your other routes:",
+ "app.use((req, res, next) => {\n res.status(404)\n .type('text')\n .send('Not Found');\n});
",
+ "Submit your page when you think you've got it right."
+ ],
+ "challengeSeed": [],
+ "tests": [
+ {
+ "text": "Logout route",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /req.logout/gi, 'You should be call req.logout() in youre /logout route'); }, xhr => { throw new Error(xhr.statusText); })"
+ },
+ {
+ "text": "Logout should redirect to the home page /",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/logout') .then(data => { assert.match(data, /Home page/gi, 'When a user logs out they should be redirected to the homepage'); }, xhr => { throw new Error(xhr.statusText); })"
+ }
+ ],
+ "solutions": [],
+ "hints": [],
+ "type": "backend",
+ "challengeType": 0,
+ "translations": {}
+ },
+ {
+ "id": "58966a17f9fc0f352b528e6d",
+ "title": "Registration of New Users",
+ "description": [
+ "Now we need to allow a new user on our site to register an account. On the res.render for the home page add a new variable to the object passed along- showRegistration: true
. When you refresh your page, you should then see the registration form that was already ctreated in your index.pug file! This form is set up to POST on /register so this is where we should set up to accept the POST and create the user object in the database.",
+ "The logic of the registration route should be as follows: Register the new user > Authenticate the new user > Redirect to /profile",
+ "The logic of step 1, registering the new user, should be as follows: Query database with a findOne command > if user is returned then it exists and redirect back to home OR if user is undefined and no error occurs then 'insertOne' into the database with the username and password and as long as no errors occur then call next to go to step 2, authenticating the new user, which we've already written the logic for in our POST /login route.",
+ "app.route('/register')\n .post((req, res, next) => {\n db.collection('users').findOne({ username: req.body.username }, function (err, user) {\n if(err) {\n next(err);\n } else if (user) {\n res.redirect('/');\n } else {\n db.collection('users').insertOne(\n {username: req.body.username,\n password: req.body.password},\n (err, doc) => {\n if(err) {\n res.redirect('/');\n } else {\n next(null, user);\n }\n }\n )\n }\n })},\n passport.authenticate('local', { failureRedirect: '/' }),\n (req, res, next) => {\n res.redirect('/profile');\n }\n);
",
+ "Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point here."
+ ],
+ "challengeSeed": [],
+ "tests": [
+ {
+ "text": "Register route and display on home",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /showRegistration:( |)true/gi, 'You should be passing the variable \"showRegistration\" as true to your render function for the homepage'); assert.match(data, /register[^]*post[^]*findOne[^]*username:( |)req.body.username/gi, 'You should have a route accepted a post request on register that querys the db with findone and the query being \"username: req.body.username\"'); }, xhr => { throw new Error(xhr.statusText); })"
+ },
+ {
+ "text": "Registering should work",
+ "testString": "getUserInput => $.ajax({url: getUserInput('url')+ '/register',data: {username: 'freeCodeCampTester', password: 'freeCodeCampTester'},crossDomain: true, type: 'POST', xhrFields: { withCredentials: true }}) .then(data => { assert.match(data, /Profile/gi, 'I should be able to register and it direct me to my profile. CLEAR YOUR DATABASE if this test fails (each time until its right!)'); }, xhr => { throw new Error(xhr.statusText); })"
+ },
+ {
+ "text": "Login should work",
+ "testString": "getUserInput => $.ajax({url: getUserInput('url')+ '/login',data: {username: 'freeCodeCampTester', password: 'freeCodeCampTester'}, type: 'POST', xhrFields: { withCredentials: true }}) .then(data => { assert.match(data, /Profile/gi, 'Login should work if previous test was done successfully and redirect successfully to the profile. Check your work and clear your DB'); assert.match(data, /freeCodeCampTester/gi, 'The profile should properly display the welcome to the user logged in'); }, xhr => { throw new Error(xhr.statusText); })"
+ },
+ {
+ "text": "Logout should work",
+ "testString": "getUserInput => $.ajax({url: getUserInput('url')+ '/logout', type: 'GET', xhrFields: { withCredentials: true }}) .then(data => { assert.match(data, /Home/gi, 'Logout should redirect to home'); }, xhr => { throw new Error(xhr.statusText); })"
+ },
+ {
+ "text": "Profile should no longer work after logout",
+ "testString": "getUserInput => $.ajax({url: getUserInput('url')+ '/profile', type: 'GET', crossDomain: true, xhrFields: { withCredentials: true }}) .then(data => { assert.match(data, /Home/gi, 'Profile should redirect to home when we are logged out now again'); }, xhr => { throw new Error(xhr.statusText); })"
+ }
+ ],
+ "solutions": [],
+ "hints": [],
+ "type": "backend",
+ "challengeType": 0,
+ "translations": {}
+ },
+ {
+ "id": "589690e6f9fc0f352b528e6e",
+ "title": "Clean Up Your Project with Modules",
+ "description": [
+ "Right now everything you have is in your server.js file. This can lead to hard to manage code that isn't very expandable.",
+ "Create 2 new files: Routes.js and Auth.js",
+ "Both should start with the following code: module.exports = function (app, db) {\n\n\n}
",
+ "Now in the top of your server file, require these files like such: const routes = require('./routes.js');
",
+ "Right after you establish a successful connect with the database instanciate each of them like such: routes(app, db)
",
+ "Finally, take all of the routes in your server and paste them into your new files and remove them from your server file. Also take the ensureAuthenticated since we created that middleware function for routing specifically. You will have to now correctly add the dependencies in that are used, such as const passport = require('passport');
, at the very top above the export line in your routes.js file.",
+ "Keep adding them untill no more errors exist, and your server file no longer has any routing!",
+ "Now do the same thing in your auth.js file with all of the things related to authentication such as the serialization and the setting up of the local strategy and erase them from your server file. Be sure to add the dependencies in and call auth(app,db)
in the server in the same spot.",
+ "Congratulations- youre at the end of this section of Advanced Node and Express and have some beautiful code to show for it! Submit your page when you think you've got it right. If you're running into errors, you can check out an example of the completed project here."
+ ],
+ "challengeSeed": [],
+ "tests": [
+ {
+ "text": "Modules present",
+ "testString": "getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /require.*(\"|').\\/routes.js(\"|')/gi, 'You should have required your new files'); assert.match(data, /mongo.connect[^]*routes/gi, 'Your new modules should be called after your connection to the database'); }, xhr => { throw new Error(xhr.statusText); })"
+ }
+ ],
+ "solutions": [],
+ "hints": [],
+ "type": "backend",
+ "challengeType": 0,
+ "translations": {}
+ }
]
}
\ No newline at end of file