Starting from:

$30

Project 4: Blogging Server on Node.js and MongoDB


Project 4: Blogging Server on Node.js and MongoDB
Change history
02/22/2018 10+30AM Added requirement for using port 3000 for the server
02/23/2018 10+05PM Added postid as a required field of /api/:username
02/28/2018 06+52PM Added clarification on Part E editor entry point
Overview
The primary task of Project 4 is to implement the website for our markdown-based
blogging service that (1) lets anyone read blogs written by our users through
public URLs and (2) lets our registered users create and update their own blogs
after password authentication. Through this process, we will learn how to develop
a back-end service using Node.JS, Express and MongoDB.
Development Environment
The development for Project 4 will be done using the same docker container that
you created in Project 3, which can be stared with the command:
$ docker start -i mean
Make sure that Node.JS and MongoDB runs fine through the following commands:
$ node --version
$ mongo -version
In addition, we will use the Express application generator to generate the server
skeleton code for this project. Please install it using the following command
$ sudo npm install -g express-generator
and make sure that it is correctly installed by running
$ express --version
Project Requirements
Our back-end blogging service should be accessible at the following URLs:
# URL method functionality
1 /
blog/:username/:p
ostid
GET Return an HTMLformatted page
that shows the
blog post
with postid written
by username.
2 /blog/:username GET Return an HTML
page that contains
first 5 blog posts
by username.
3 /login?
username=:userna
me
&password=:pass
word
&redirect=:redirec
t
GET If
either username o
r password is
missing or if they
don't match to our
record, return an
HTML page with
the username and
password form
fields. If they
match, set an
authentication
session cookie in
JSON Web Token
(JWT), and
redirect
to redirect.
4 /api/:username GET This is the REST
API used by the
Angular blog
editor to retrieve
all blog posts
by username
5 /
api/:username/:po
stid
GET, POST, PUT,
DELETE
This is the REST
API used by the
Angular blog
editor to perform
a CRUD operation
on the user's blog
post
6 /edit/ GET The main URL
from which the
user can access
the Angular blog
editor app
# URL method functionality
1 /
blog/:username/:p
ostid
GET Return an HTMLformatted page
that shows the
blog post
with postid written
by username.
2 /blog/:username GET Return an HTML
page that contains
first 5 blog posts
by username.
3 /login?
username=:userna
me
&password=:pass
word
&redirect=:redirec
t
GET If
either username o
r password is
missing or if they
don't match to our
record, return an
HTML page with
the username and
password form
fields. If they
match, set an
authentication
session cookie in
JSON Web Token
(JWT), and
redirect
to redirect.
4 /api/:username GET This is the REST
API used by the
Angular blog
editor to retrieve
all blog posts
by username
5 /
api/:username/:po
stid
GET, POST, PUT,
DELETE
This is the REST
API used by the
Angular blog
editor to perform
a CRUD operation
on the user's blog
post
6 /edit/ GET The main URL
from which the
user can access
the Angular blog
editor app
!.
#.
$.
g.
h.
!.


#.
$.
# URL method functionality
1 /
blog/:username/:p
ostid
GET Return an HTMLformatted page
that shows the
blog post
with postid written
by username.
2 /blog/:username GET Return an HTML
page that contains
first 5 blog posts
by username.
3 /login?
username=:userna
me
&password=:pass
word
&redirect=:redirec
t
GET If
either username o
r password is
missing or if they
don't match to our
record, return an
HTML page with
the username and
password form
fields. If they
match, set an
authentication
session cookie in
JSON Web Token
(JWT), and
redirect
to redirect.
4 /api/:username GET This is the REST
API used by the
Angular blog
editor to retrieve
all blog posts
by username
5 /
api/:username/:po
stid
GET, POST, PUT,
DELETE
This is the REST
API used by the
Angular blog
editor to perform
a CRUD operation
on the user's blog
post
6 /edit/ GET The main URL
from which the
user can access
the Angular blog
editor app
Note:
The URL patterns 1-3 should be publicly accessible by anyone. No prior user
authentication should be required to access these URLs. More detailed
requirement for the URL pattern 2 will be given in Part B.
The URL patterns 4-6 should be protected behind authentication. For the URL
patterns 4-5, if the authenticated username is different from the username in
the URL, the server should reply with "401 (Unauthorized)" status code. For
URL pattern 6, if the user has already been authenticated, the server should
return the Angular app that we implemented in Project 3. Otherwise, the
server should redirect the request to the URL pattern 3, so that the user can
authenticate themselves.
The JWT session cookie set by the server after the user authenticates
themselves through the URL pattern 3 should follow the format described
later in Part C.
More detailed specifications on what the server should do for the URL
patterns 4-5 are given later in Part D.
The implemented server should listen on port 3000 for HTTP requests.
All blog posts and the users' authentication credentials should be stored in the
MongoDB server. The MongoDB server should have at least the following two
collections, "Posts" and "Users", in the database "BlogServer". The two collections
must have two initial documents shown below:
Collection: Posts
{ "postid": 1, "username": "cs144", "created": 1518669344517, "modified":
1518669344517, "title": "Title 1", "body": "Hello, world!" }
{ "postid": 2, "username": "cs144", "created": 1518669658420,
"modified": 1518669658420, "title": "Title 2", "body": "I am here." }
The first collection "Posts" stores all blog posts created and saved by our
users. As users write more blog posts, more documents should be inserted
into this collection. Note that "created" and "modified" fields of the two
documents are all integers, whose values are milliseconds since the the Unix
epoch (Jan 1, 1970 UTC).
Collection: Users


g.
{ "username": "cs144", "password": "$2a$10$2DGJ96C77f/
WwIwClPwSNuQRqjoSnDFj9GDKjg6X/PePgFdXoE4W6" }
{ "username": "user2", "password":
"$2a$10$kTaFlLbfY1nnHnjb3ZUP3OhfsfzduLwl2k/gKLXvHew9uX.
1blwne" }
The second collection "Users" stores the users' authentication credentials.
They should be used for authenticating any user to our server through the URL
pattern 3. Note that users' passwords must NEVER be stored in plaintext.
Instead, we have to store them only after we apply a cryptographic one-way
hash function. This ensures that even if a hacker breaks into our system and
gets a hold of our database, they won't be able to obtain the users' passwords
easily since it is time-consuming to recover the plaintext passwords from the
hash values. The downside of this approach is that when a user tries to login,
we will have to apply the same cryptographic hash function to the userprovided password and then match the equivalence of this hash value to what
is stored in our database. This can potentially increase the computational
overhead of authenticating a user, but given the potential security risk of
saving plaintext passwords, it is the cost that we are willing to pay. In our case,
we applied bcrypt hash function to each user's password ("password" for
"cs144" and "blogserver" for "user2", respectively) using the bcrypt module of
node.js.
Finally, you must also change our Angular Markdown Editor of Project 3, so that it
uses the REST APIs of our server -- the URL patterns 4 and 5 -- to manage the
user's posts, and make it available at the URL /edit/.
Part A: Create Initial MongoDB Data
In Project 4, all blog posts must be managed by MongoDB. Unlike MySQL,
MongoDB doesnʼt have the concept of schema. All types of data are saved
as documents in a collection. Since MongoDB document is essentially a JSON
object, it is often a preferred back-end data storage engine for JavaScript-based
development.
Although our Docker container has MongoDB preinstalled, the database server
does not start automatically when you start the container. To start the server, run:
$ sudo mongod --fork --logpath /var/log/mongodb/mongodb.log
When you are done, you can stop the server with:
$ sudo mongod --shutdown
While the MongoDB server is running, you can start the "MongoDB command-line
!.


#.


shell" by:
$ mongo
Once you are inside the shell, you can issue most MongoDB commands
interactively. Go over class notes on MongoDB to review the basic MongoDB
commands. If needed, review online tutorials on MongoDB, such as this one.
Now write a script named db.sh that includes the sequence of mongodb shell
commands that load the following documents into the two collections, "Posts" and
"Users", in the "BlogServer" database:
Collection: Posts
{ "postid": 1, "username": "cs144", "created": 1518669344517, "modified":
1518669344517, "title": "Title 1", "body": "Hello, world!" }
{ "postid": 2, "username": "cs144", "created": 1518669658420,
"modified": 1518669658420, "title": "Title 2", "body": "I am here." }
Collection: Users
{ "username": "cs144", "password": "$2a$10$2DGJ96C77f/
WwIwClPwSNuQRqjoSnDFj9GDKjg6X/PePgFdXoE4W6" }
{ "username": "user2", "password":
"$2a$10$kTaFlLbfY1nnHnjb3ZUP3OhfsfzduLwl2k/gKLXvHew9uX.
1blwne" }
We also provide two JSON files that contain the above
documents, posts.json and users.json, in case they are helpful.
Note that "created" and "modified" fields of the first two post documents are all
integers, whose values represent the milliseconds since the the Unix epoch (Jan 1,
1970 UTC). You must store all date fields in this format. Also recall that the above
password values are obtained by applying the bcrypt cryptographic one-way hash
function.
Note that your provided script db.sh will be executed through the following
command
$ mongo < db.sh
before we grade your submission to initialize the MongoDB database for the
server.
Notes on CR/LF issue: If your host OS is Windows, you need to pay attention to
how each line ends in your script file. Windows uses a pair of CR (carriage return)
and LF (line feed) characters to terminate lines, but Unix uses only a LF character.
Therefore, problems may arise when you feed a text file generated from a
Windows program to a Unix tool such as mongo. If you encounter any wired error
when you run your script, you may want to run the dos2unix command in the
container on your script file to fix line end characters.
Part B: Implement Public HTML Blog Web Pages
As the second task of Project 4, we now implement the public URLs by which any
user can view blog posts published on our website. In particular, we will implement
a server that returns an HTML page to an HTTP request to the following URL
patterns:
# URL method functionality
1 /
blog/:username/:p
ostid
GET Return an HTMLformatted page
that shows the
blog post
with postid written
by username.
2 /blog/:username GET Return an HTML
page that contains
first 5 blog posts
by username. If
the user has more
than 5 posts, the
page should
contain a "next"
button that link to
the next 5 posts
by the user.
We use node.js JavaScript runtime engine and its express module to implement
our back-end server. Node.js is built on Chrome's V8 JavaScript engine, which
uses an event-driven, non-blocking I/O model to make it lightweight and efficient.
Express is a module that provides an easy-to-use routing mechanism, HTML
template integration, and third-party middleware integration. You may want to go
over the class lecture note on node.jsto brush up on node.js and express. If you
need more detailed instruction on how to use them, online tutorials like this can be
helpful. Express web site has more detailed documentation on routing and using a
template engine.
Generate Server Skeleton Code
To generate our web server, we will use the express application generator. Initialize
the project under a project directory (e.g. blog-server):
$ express -e blog-server
Here -e option makes the generated code use the "EJS" template engine for
generating HTML pages from JSON. You are welcome to use other template
engine for your development, but our project spec gives instructions for "EJS".
When the above command is executed, you see the following folder and file
structure within the blog-server directory:
blog-server
+- bin
+- public
+- routes
+- views
+- package.json
+- app.js
Here the app.js file serves as the entrance of the whole project. If you define
other .js files, you need to reach them starting from app.js. The package.json file
contains some meta information on the project including its "package
dependencies". When you add new dependencies to this file and run npm
install, npm will install all dependent modules into the subdirectory node_modules.
The public directory contains static resources that are made available on the
developed web site, like HTML, CSS, JavaScript, and image files.
The views directory contains HTML template files. The routes directory contains
"middleware" that processes the requests. The bin directory contains the
executable file. As you develop your code, you are welcome to add or modify any
directory or file as needed.
Make sure that the generated code works properly on our container by executing
the following command:
$ npm start
The npm start command executes "test" script specified in package.json (which
is node ./bin/www in the auto-generated package.json file). Open a web browser
on your host machine and make sure that you see a page similar to the following
image at http://localhost:3000/:
Install mongodb and commonmark Modules
To generate public HTML blog pages using node.js, express, and MongoDB, we
need a MongoDB client library for node.js and a markdown-to-HTML rendering
library. We will use the official MongoDB driver for node.js and the commonmark.js
library for this purpose. Install these two packages using npm
$ npm install --save mongodb
$ npm install --save commonmark
Make sure that you add the --save option, so that the packages are added as
dependencies to package.json.
You must be familiar with commonmark API by now from earlier projects, but you
may not be familiar with the MongoDB native client API yet. Go over MongoDB
native drive quick start tutorial to learn the basics and read the MongoDB tutorial
on CRUD operations to learn more details.
Note: Due to a strange interaction between node, Docker container, and shared
folder, you may sometimes get an error similar to the following when you
run node or npm:
path.js:1177
cwd = process.cwd();
^
Error: ENOENT: no such file or directory, uv_cwd
If you get the above error, you can "fix it" by cd ../, cd back, and try again.
Learn the Template Syntax
The responses to the two URL patterns shown above should be all in HTML. For
generating an HTML response, you can use a template engine. When you use a
template engine, you just need to write a static "template file", and "render" the
final HTML response by combining the template with data.
EJS (Embedded JavaScript) uses a template syntax very similar to Java
ServePages (JSP). Like JSP, you can use the standard HTML tags in the template,
and sprinkle your JavaScript code inside <% ... %, <%= ... %, or <%- ...
% tags. <%= ... % or <%- ... % can include any expression and is replaced with
the output string of the expression. The difference between the two is that <%= ...
% escapes HTML tags in the output, so that the HTML tags are displayed as
strings, while <%- ... % does not escape HTML tags, so that the browser can
interpret them as HTML tags. <% ... % can include any arbitrary JavaScript code,
not just expressions.
Here is an example of a valid EJS template:
<ul
<% for(let i = 0; i < books.length; i++) { %
<li<%- books[i].isbn %</li
<% } %
!.
#.
$.
!.
#.
$.
g.
</ul
For more EJS examples, look at the generated template files in the views directory.
You may also want to go over an online tutorial like this to learn more on EJS.
Implement /blog/:username/:postid and /blog/:username
Now that you know the basics to implement Part B, go ahead and implement it.
The responses from these two URL patterns must meet the following
requirements:
All blog posts returned from the two URL patterns should be rendered in
HTML from markdown using the commonmark.js module, both title and body.
The second URL pattern /blog/:username must return the first 5 posts
by username. When there are more posts from the user the returned page
must contain a "next" link, which points to a page with the next 5 posts by the
user. Make sure that the "next" link is implemented as an HTML <a element
with id="next" and href pointing to the URL of the "next page".
The second URL pattern /blog/:username must take an optional query string
parameter start=:postid, like
/blog/cs144?start=3
When this optional query string parameter exists, the response must include
the next 5 posts by cs144 whose postid is 3 or above.
Note:
In implementing this part, remember that a request can be "routed" to a
callback function through app.METHOD(URL, callback), like app.get('/
blog/:username', callback). Inside the callback function, you can reference the
HTTP request through the first req parameter, and you can generate the
response through the second res parameter.
In our project, all dates are stored as a number in MongoDB, which represents
milliseconds since the the Unix epoch (Jan 1, 1970 UTC). You can convert a
JavaScript Date object to this number using its getTime() method. Conversely,
you can convert this number to a Dateobject by passing it as the constructor
parameter of Date or by calling a date object's setTime() method.
Please remember that all MongoDB commands must be
executed asynchronously either using callback functions or using
a Promise object (with await keyword).
To minimize the overhead from creating a connection to the database server,
we strongly recommend that you create a connection to the MongoDB server
when the your application starts up and reuse the created connection for all
MongoDB commands. If you are unclear about how this can be done, see this
page (This page is a slightly modified version of the original blog post
g.
published here.) Note that if you decide to add app.listen() to app.js file, you
will have to remove the call to listen() from the "bin/www" file.
Part C: Implement User Login Page
In this part, we will implement the user login page available at the following URL:
# URL method functionality
1 /login?
username=:userna
me
&password=:pass
word
&redirect=:redirec
t
GET If
either username o
r password is
missing or if they
don't match to our
record, return an
HTML page with
the username and
password form
fields. If they
match, set an
authentication
session cookie in
JSON Web Token
(JWT), and
redirect
to redirect.
Recall that our MongoDB server stores all users' credentials in the "Users"
collection of the "BlogServer" database. In addition, recall that the stored user
passwords are all hash values obtained by applying the bcrypt hash function.
Therefore, to match a user's stored password against what she provides during
authentication, you will need to use node.js bcrypt module. Install the module
through the following command:
$ npm install --save bcrypt
Read the bcrypt package page to learn how you can use it to compare a user's
password against a hash value.
JSON Web Token (JWT)
Once the user's authenticity is established through the user-provided password,
our server must establish a "authenticated session", so that it can recognize that
any future request coming from the same browser comes from the authenticated
user. There are a number of ways to implement this. In this project, you must
!.
#.
$.
use JSON Web Token (JWT) for this purpose. If you are not familiar with JWT, go
over the JWT introduction page to learn what it is and how it can be used.
In your implementation, once the user is authenticated, you must set a transient
session cookie (a cookie that has no expiration date, so that it is forgotten once
the browser is closed) whose name is jwt and whose value is the following JWT:
Its header must have two claims, 'alg' (algorithm) and 'typ' (type), with the
following values:
{
“alg”: “HS256”,
“typ”: “JWT”
}
Its payload must have two claims, 'exp' (expiration time) and 'usr' (user)
{
“exp”: expiration,
“usr”: “username”
}
where expiration should be two hours from now (in seconds since Unix epoch,
Jan 1, 1970) and username should be the authenticated username. Note that
the unit of time here is seconds not milliseconds. According to JWT standard,
this is how the expiration time should be represented.
The signature must be generated using the HS256 algorithm (HMAC-SHA256
hash function) with the following secret key:
C-UFRaksvPKhx1txJYFcut3QGxsafPmwCY6SCly3G6c
Once this JWT cookie is set, our server will be able to recognize that any future
request from the browser comes from the authenticated user username.
You can use the jsonwebtoken module to construct a JWT. Install it with the
following command
$ npm install --save jsonwebtoken
and learn how to use it by going over examples in the jsonwebtoken module page.
Now implement the login page. Remember that after a successful authentication
by the user (i.e., the user's username and password match), the server should
generate an appropriate JWT, set it as the value of the cookie jwt, and redirect the
request to the redirect query parameter in the request. If the authentication fails,
(username or password is missing or they don't match with our record), the server
must return a page with the username and password input box, so that they user
!.
#.
$.
can try again.
Remember that the user "cs144"'s password is "password" and "user2"'s
password is "blogserver" in testing your authentication page.
Part D: Implement Blog-Management REST API
In this part, you will have to implement the REST API that will be used by our
Angular-based editor to save, retrieve, update, and delete the blog posts from the
server.
# URL method functionality
1 /api/:username GET This is the REST
API used by the
Angular blog
editor to retrieve
all blog posts
by username
2 /
api/:username/:po
stid
GET, POST, PUT,
DELETE
This is the REST
API used by the
Angular blog
editor to perform
a CRUD operation
on the user's blog
post
Here are more detailed descriptions of the above API:
GET /api/:username: The server should return all blog posts by username.
The returned posts should be included in the body of the response as an array
in JSON even if the user has zero or one post. Each post in the array must
have at least five fields, postid, title, body, created, and modified (case
sensitive). The response status code should be "200 (OK)".
GET /api/:username/:postid: The server should return the blog post
with postid by username. If such a post exists, the response status code
should be "200 (OK)", and the post should be included in the body of the
response in JSON with at least four fields, title, body, created,
and modified (case sensitive). If not, the response status code should be "404
(Not found)".
POST /api/:username/:postid: When the server gets this request, it must
insert a new blog post with username, postid, title, and bodyfrom the request.
The request must include title and body in its body in JSON.
$.
g.
h.
É.
Ñ.
Ö.
The created and modified fields of the inserted post should be set to the
current time. If the insertion is successful, the server should reply with "201
(Created)" status code. If a blog post with the
same postid by username already exists in the server, the server should not
insert a new post and reply with "400 (Bad request)" status code.
PUT /api/:username/:postid: The request must include title and body in its
body in JSON. When the server gets this request, it must update the existing
blog post with postid by username with the title and body values from the
request. The modified field should be updated to the current time as well. If
the update is successful, the server should reply with "200 (OK)" status code.
If there is no blog post with postid by username, the server should reply with
"400 (Bad request)" status code.
DELETE /api/:username/:postid: When the server gets this request, the
server must delete the existing blog post with postid by username from the
database. If the deletion is successful, the server should reply with "204 (No
content)" status code. If there is no such post, the server should reply with
"400 (Bad request)" status code.
All dates transmitted should be in milliseconds since the the Unix epoch
(Jan 1, 1970 UTC) in number type.
If a request does not meet our requirements (such as not formatting data in
JSON, not including required data, etc.), the server must reply with "400 (Bad
request)" status code.
This REST API must be protected behind authentication. That is, if the request
to this API does not contain a valid jwt cookie with matching username (i.e., if
the jwt cookie is not included in the HTTP header, if the included jwt has
expired, or if the username in jwt does not match the username in the URL),
the server must reply with "401 (Unauthorized)" status code.
Now add the appropriate routing instruction to your app and implement the above
API. Make sure that the implementation works as intended before you proceed.
You can test your implementation using the curl command, which can send any
HTTP request to a server from command line.
Once you made sure that everything works fine, protect this REST API behind
authentication. That is, before you process any request to this API, first make sure
that the request contains a valid jwt cookie with the matching username.
Part E: Update Angular Editor Client for Server-based Storage
!.
#.
$.
g.
h.
É.
After you finish implementing the REST API, the final step is to connect it with the
Angular editor you developed in Project 3. Your previous implementation
of BlogService managed all blog posts locally through localStorage. Your new
implementation must use the REST API of Part D to manage them at the server.
Roughly, your new implementation of BlogService should perform the following
actions in its methods:
fetchPosts(): void – This method must “populate” the posts property by
retrieving all blog posts of the current user (whose username can be obtained
from the jwt cookie). Send a GET request to /api/:username after setting up
the response event handler, which populates posts property from the
response.
getPosts(): Post[] – It is unlikely that you have to modify this method in
Project 4. This method simply returns posts.
getPost(id: number): Post – It is unlikely that you have to modify this method
in Project 4. Find the post with postid=id from posts and return it.
newPost(): Post – Create a new post with a new postid, an empty title and
body, and the current creation and modification times, add it to posts, send
a POST request to /api/:username/:postid (after setting up the response event
handler), and return the newly created post.
The response handler should do nothing if the response status code is "201
(Created)". Otherwise, it should delete the newly created post from posts,
display an alert message saying that there was an error creating a new post at
the server, and navigate to /, the "list pane" of the editor.
updatePost(post: Post): void – From posts, find a post whose postid is the
same as post.postid, update its title and body with the passed-in values,
change its modification time to now, and send a PUT request to /
api/:username/:postid (after setting up the response event handler). If no such
post exists, do nothing.
The response event handler should do nothing if the response status code is
"200 (OK)". Otherwise, it should display an alert message saying that there
was an error updating the post at the server, and navigate to the "edit view" of
the post.
deletePost(postid: number): void – From posts, find a post whose postid is
the same as the passed in value, delete it from posts, and send
a DELETE request to /api/:username/:postid (after setting up the response
event handler). If no such post exists, do nothing.
The response event handler should do nothing if the response status code is
É.
!.
#.
"204 (No content)". Otherwise, it should display an alert message saying that
there was an error deleting the post at the server, and navigate to /, the "list
pane" of the editor.
The HTTP request to the server can be sent through various mechanisms, such
as XMLHttpRequest object, Fetch or HttpClient object in Angular. If you decide to
use HttpClient, you may need a basic understanding on the concept of
"observable" (or event stream) and "observer" (or subscriber). If you feel that you
need to learn more on this, there exist a number of good online introductory
materials, such as observer pattern Wikipedia entry or this tutorial on reactive
programming.
Within a browser, cookies are accessible through document.cookie. Once you
obtain the JWT from the cookie, you can obtain the authenticated username from
it. You can use the jsonwebtoken module for this purpose, so you may want to
install the module in your Angular project using the following command:
$ npm install --save jsonwebtoken
Eventually, your revised Angular app must be deployed to your node/express
server, but during the development of Part E, you may want to run your Angular
app through the "ng serve" command, so that you can test, revise, and iterate.
Unfortunately, running Angular app through ng servehas a few unintended
consequences:
You have to run two servers -- the node/express server through npm start and
the angular app through ng serve --host 0.0.0.0 -- within the same container.
Running two servers simultaneously can be done by executing the two
commands in the background like the following:
$ npm start &
$ ng serve --host 0.0.0.0 &
If you are not familiar with the Unix process control and job management,
read the Process section of our Unix tutorial.
Your Angular app is loaded from http://localhost:4200 but your Express server
runs at http://localhost:3000. This means that your Angular app may not be
able to use the JWT cookie set by the Express server due to cross-origin
restrictions, which won't be the case once your Angular app is deployed to the
Express server. To get around this problem, you have to allow third-party
cookies and may use Chrome's --disable-web-security --user-data-dir option.
To use this option, quit all running instances of your Chrome browser, and
start a new instance of Chrome by executing:
(On macOS) $ open -a Google\ Chrome --args --disable-web-security --
#.
user-data-dir
(On Windows) $ start chrome --disable-web-security --user-data-dir
from a terminal.
In addition, when your Angular app is deployed to the Express server, the
server will ensure that the user is authenticated before they can access the
Angular app. Unfortunately, this is not the case when your app is loaded from
its own server; when your Angular app runs, it may not have a proper JWT
cookie during development. Therefore, you will have to explicitly visit the
Express server's login page /login first, authenticate yourself, and obtain a
proper JWT cookie from the server before you load your Angular app.
Otherwise, your browser (and Angular app) won't have a proper JWT cookie to
access the Express server.
Due to all these complexities, we recommend disabling the REST-API
authentication checking mechanism of your Express server while you are
developing Part E. This will make most (but not all) headaches from JWTcookie-related issues go away.
Note: When you run your Angular app in Chrome, Chrome developer console will
throw an error for every 4XX or 5XX response from a server. Unfortunately, there
doesn't seem to be any way to "catch" it to make it disappear. As long as you
handle these responses according to our spec, you won't have to worry about this
Chrome console error.
Once you finish revising BlogService, build the final Angular application by the
command ng build and place the produced files in dist/ to the edit subdirectory of
your Express server's public folder. Before you build your Angular app, make sure
to modify the base URL of the app to /edit/ by changing the base tag in the
file src/index.html
<base href="/edit/"
Note: To simplify this part of the project, you may assume that the first entry point
to the blog post editor is always /edit/. That is, users never access the editor
through the edit page /edit/#/edit/:postid or the preview page /edit/#/
preview/:postid as the first entry point. Therefore, you may assume that the user's
all blog posts are ready and available by the time either Edit or Preview
Components are rendered.
Again, remember that any access to the /edit/ directory should be protected
behind authentication. If any request to the URL pattern /edit/does not include a
!.
#.
valid JWT cookie, the request should be redirected to the login page /login?
redirect=/edit/.
What to Submit
For this project, you will have to submit two zip files:
project4.zip: You need to create this zip file using the packaging script
provided below. This file will contain all source codes that you wrote for Parts
A through D, and the Angular application built from the modified Angular
source code.
project3.zip: You must create project3.zip file again using the packaging script
of Project 3, including all the changes that you made as part of Part E of this
project.
Creating project4.zip
Before you create the submission zip file for Project 4, please make sure that your
server can be executed simply by running the following sequence of commands at
the project root directory:
$ mongo < db.sh
$ npm start
Since this is how our grader will run and test your code, it is essential that your
code runs fine without any problem through the above commands. In particular,
please make sure that your modified Angular is in the /edit/ subdirectory of your
public folder, so that it is available when we run your server. We won't be building
and/or copying your Angular code separately. You have to place them in the
correct directory of your submission.
You may assume that the MongoDB server is running with no documents inside
the "BlogServer" database when your your server is executed in the grader's
machine.
After you have checked there is no issue with your project, you can package your
work by running our packaging script. Please first create the TEAM.txt file. This file
must include the 9-digit university ID (UID) of every team member, one UID per
line. No spaces or dashes. Just 9-digit UID per line. If you are working on your
own, include just your UID. Please make sure TEAM.txt, db.sh and package.sh
are placed in the project-root directory, blog-server, like this:
blog-server
+- bin
+- public
+- routes







+- views
+- ...
+- package.json
+- app.js
+- db.sh
+- package.sh
+- TEAM.txt
When you execute the packaging script like ./package.sh, it will check whether a
few mandatory files are there, and package everything within the project directory
(except the files in node_modules/) and create a file named project4.zip.
If everything goes smoothly, you will see something like the following:
[SUCCESS] Created '/home/cs144/shared/blog-server/project4.zip', please submit
it to CCLE.
Please only submit this script-created project4.zip to CCLE. Do not use any
other ways to package or submit your work!
Creating project3.zip
While we won't be compiling and testing your modified Angular Markdown Editor
ourselves --- for grading, we will simply use the prebuilt Angular code that you
include in project4.zip --- we still want to get a working version of your modified
source code for reference. Please run the packaging script of Project 3 within your
project 3 folder that includes your Part E modification, create a
new project3.zip file and submit it together with project4.zip to CCLE.
Grading Criteria
The grading of Project 4 will be based on the following aspects:
The server can work: You will get 0 if your server fails to work with npm start!
Interaction with Database: The grader should be able to load the initial
documents into the two collections using your db.sh script as is described in
this spec. All the posts should be stored into and retrieved from MongoDB.
Functionality: It should implement all the specified functions, i.e. list, insert,
read, update and delete in the required way. You must implement REST APIs.
URL Navigation: Users should be able to visit the corresponding contents by
directly typing the corresponding URL as is described in this spec.
Access Control: The HTMP pages must be public; while the editor and REST
API can be accessed only after login.
Error Handling: Your server should handle different kinds of errors properly.
Other Requirements in Description: For example, the next button in the /

blog/:username page, which shows 5 posts each time.

More products