COMP3322A Modern Technologies on World Wide Web Assignment 2 20
Learning Outcomes 1, 2
Overview
In this assignment, we are going to develop a simple singlepage social photo sharing application iAlbum using the MERN stack MongoDB, Express.JS, ReactJS and Node.js. The main work flow of iAlbum is as follows.
Upon loading, the sketch of the page is shown in Fig. 1
Fig. 1
After a user has logged in, the sketch of the page is in Fig. 2. A list of friend albums is shown in the left division.
Fig. 2
After clicking My Album, the sketch of the page is in Fig. 3. The photos in the users own album is displayed in the right division, together with messages of who liked a photo, deletion buttons, and new photo selectionupload buttons.
Fig. 3
Each photo is clickable. After clicking a photo, the photo will be enlarged as in Fig. 4, together with the liked message and the deletion button. When the cross is clicked, the page returns to the view shown in Fig. 3.
Fig. 4
When a friends album is selected in the lefthand list, the page view becomes one in Fig. 5. Photos of the friend are shown in the right division, together with messages of who liked a photo and the like buttons.
Fig. 5
Similarly, when a photo is clicked, the photo will be enlarged as in Fig. 6, together with the liked message and the like button. When the cross is clicked, the page returns to the view shown in Fig. 5.
Fig. 6
We are going to achieve this application by implementing code in a backend Express app and a frontend React app.
Express app: app.js
.routes albums.js
React app: .srcApp.js
.srcindex.js .srcApp.css
Task 1. Backend Web Service
We implement the backend web service logic using Express.js. The web service is accessed at http:localhost:3002xx.
Preparations
1. Following steps in Lab 6, install the Node.js environment and the Express framework, create an Express project named AlbumService, and install dependencies for MongoDBmonk.
2. Following steps in Lab 6, install MongoDB, run MongoDB server, and create a database assignment2 in the database server.
Insert a number of records to a userList collection in the database in the format as follows.
We will assume that user names are all different in this application.
db.userList.insertusername: Eddie, password: 123456, friends:Ken, Alice, Bill
Create a folder uploads under the public folder in your project directory. Copy a few photos to the uploads folder due to our simplified implementation to be outlined later, we will only use .jpg photo files in this assignment. Insert a few records to a photoList collection in the database in the format as follows, each corresponding to one photo you have copied to the uploads folder. Here userid should be the value of id generated by MongoDB for the photo owners record in the userList collection, which you can find out using db.userList.find.
Note that copying photos to uploads folder and inserting photo records into the photoList collection, as we do above, are only for testing purpose. We will implement the functionality of uploading photos and inserting their records to the database using iAlbum.
For implementation simplicity, in this assignment, we do not store uploaded photos in MongoDB. Instead, we store them in the harddisk under the .publicuploads folder, and store the path of a photo in the photoList collection only, using which we can identify the photo in the uploads folder.
Task 1. Implement backend web service logic AlbumServiceapp.js, AlbumServiceroutesalbums.js
app.js 10 marks
In app.js, load the router module implemented in .routesalbums.js. Then add the middleware to specify that all requests for http:localhost:3002 will be handled by this router.
Add necessary code for loading the MongoDB database you have created, creating an instance of the database, and passing the database instance for usage of all middlewares.
Also load any other modules and add other middlewares which you may need to implement this application.
Add the middleware to serve requests for static files in the public folder.
We will let the server run on the default port 3002 and launch the Express app using
command node app.js.
.routesalbums.js 32 marks
db.photoList.inserturl: http:localhost:3002uploads1.jpg, userid: xxxxxxx, likedby:Ken, Alice
In albums.js, implement the router module with middlewares to handle the following endpoints:
1. HTTP GET requests for http:localhost:3002init. The middleware checks if the userID cookie has been set for the client. If not, send an empty string back to the client. Otherwise, retrieve username of the current user according to the value of the userID cookie, and username and id of friends of the current user from the userList collection in the database; send all retrieved information as a JSON string to the client if the database operation is successful, and the error message if failure. You should decide the format of the JSON string and retrieve data accordingly in the clientside code to be implemented in Task 2.
2. HTTP POST requests for http:localhost:3002login. The middleware should parse the body of the HTTP POST request and extract the username and password carried in request body. Then it checks whether the username and password match any record in the userList collection in the database. If no, send Login failure in the body of the response message. If yes, set a cookie with the key of userID, the value of this user records id, and a maxAge of 1 hour; send the username and id of all friends of the loggedin user as a JSON string in the body of the response message if database operation is successful and the error message if failure. Again, you should design the format of the JSON string.
3. HTTP GET requests for http:localhost:3002logout. The middleware should unset the userID cookie, and send an empty string back to the user.
4. HTTP GET requests for http:localhost:3002getalbum:userid. If the userid in the URL is 0, the middleware should retrieve id, url and likedby array of all photos of the current user from the photoList collection in the database, based on the userid stored in the userID cookie. Otherwise, retrieve id, url and likedby array of all photos of the friend from the photoList collection based on the userid in the URL. Send all retrieved information as a JSON string in the body of the response message if database operation is successful, and the error message if failure. You should decide the format of the JSON string to be included in the response body.
5. HTTP POST request for http:localhost:3002uploadphoto. Store the photo carried in the request body in .publicuploads folder and insert a record of the photo in the photoList collection in the database.
To read and write files in the ExpressNode.js framework, you will need the fs module https:nodejs.orgapifs.html. Load the module in albums.js. To store the received photo in a path e.g., .publicuploads1.jpg, a simple approach is to use req.pipefs.createWriteStreampath; .
Refer to https:nodejs.orgapifs.htmlfsfscreatewritestreampathoptions for fs.createWriteStream and
https:nodejs.orgapistream.htmlstreamreadablepipedestinationoptions for pipe.
Since in our simplified clientside implementation detailed in Task 2, the client does not send the file name of the uploaded photo but only the image file, you can create a name of your choice for the newly uploaded photo. For example, you can generate a random number x and name the file x.jpg recall that we assume only .jpg files are uploaded. Then the photo record to be inserted to the photoList collection should be url: http:localhost:3002uploadsx.jpg, userid: xxxxxxx, likedby:, where userid is the value of the userID cookie.
The middleware should send a JSON string containing the id and url of the photo in the photoList collection to the user if success, and the error message if failure.
6. HTTP DELETE requests for http:localhost:3002deletephoto:photoid. The middleware should delete the photo record from the photoList collection, corresponding to the id value in the URL, and remove the photo file from .publicuploads folder Hint: you may find fs.unlink useful https:nodejs.org apifs.htmlfsfsunlinkpathcallback. Return an empty string to the client if success and the error message if failure.
7. HTTP PUT requests for http:localhost:3002updatelike:photoid. The middleware should update the likedby array of the photo record in the photoList collection, corresponding to the id value in the URL. Especially, use id of the current user in the userID cookie to find out the username of the current user from the userList collection, and then use the db.collection.update method to update the photo record in the photoList collection. Return the updated likedby array to the client if success and the error message if failure.
8. Add the code for enabling CORS in albums.js, similar to what you did in Lab 8.
Task 2 Frontend React App
Implement the frontend as a React app. The app is accessed at http:localhost:3000.
Preparations
Following steps in Lab 8, create a React app named MyApp and install the jQuery module in
the React app.
Implement the React app MyAppsrcindex.js,
MyAppsrcApp.js, MyAppsrcApp.css index.js 3 marks
Modify the generated Index.js in the .src folder of your react app directory, such that it renders the component you create in App.js in the root division in the default index.html.
App.js 40 marks
App.js should import the jQuery module and link to the style sheet App.css.
Design and implement the component structure in App.js, such that the frontend page views and functionalities as illustrated in Figures 16 can be achieved. You can decide the components to create, in order to implement the React app.
Hints:
1. You can use conditional rendering to decide what component to be displayed on the
page view. For example, in the root component you are creating in App.js, you may use a state variable to indicate if a cookie has been set for the current user i.e., the user has logged in or not, and then render the component presenting the respective page view accordingly. Initialize the state variable to indicate that no cookie has been set, and update it when the user has successfully logged in and logged out, respectively.
2. Page load. When the browser loads the page from the React App, send an AJAX HTTP GET request for http:localhost:3002init: 1 if an empty string is received from the server, a page as shown in Fig. 1 should be displayed; 2 if a JSON string carrying username of the loggedin user and id and username of the friends is received, a page as shown in Fig. 2 should be displayed. Especially, the username of the loggedin user is to be shown in the message Hello username! in the header area.
3. Log in. When handling the click event on the log in button, check if both the username and password input textboxes are nonempty: if so, send an AJAX HTTP POST request for http:localhost:3002login, carrying the input username and password in the request body; otherwise, show an alert popup box stating You must enter username and password. If a Login failure response is received, the page view remains the one in Fig. 1, except for displaying Login failure in the header area in addition. If a JSON string containing the username and id of friends is received, display the page as shown in Fig. 2.
4. Log out. When handling a click event on the log out button, send an AJAX HTTP GET request for http:localhost:3002logout. Display the page view as in Fig. 1 when an empty string is received from the server.
5. When handling a click event on My Album in the album list, send an AJAX HTTP GET request for http:localhost:3002getalbum0. When the JSON string containing id, url and likedby array of the current users photos is received, display the photos following the sketch in Fig. 3. Especially, each photo is an img element with a src
attribute pointing to the url of the respective photo, and an id attribute recording id of the photo. If the likedby array of a photo is not empty, display a message xx liked the photo! underneath the photo; otherwise, do not display the message. Display a Delete button under each photo. You can decide the display size of the photos and the number of photos to display in each row on the page. Add a file upload input element http:www.w3schools.comjsrefdomobjfileupload.asp and an Upload Photo button at the bottom of the right division. Note that the texts to display on and behind the file upload input button may differ depending on the browser you use to render the file upload input element.
6. When handling a click event on a friends xxs Album entry in the album list, send an AJAX HTTP GET request for http:localhost:3002getalbumuserid, where the userid is the id of the friend in the userList collection in the database. When the JSON string containing id, url and likedby array of the friends photos is received, display the page view as shown in Fig. 5. Note that the album entry in the list in the left division, whose photos are being displayed in the right division, should be highlighted.
7. When handling a click event on the Upload Photo button, check if a file has been selected with the file upload input element Hint: you may find files property of
the input FileUpload object useful http:www.w3schools.comjsrefdomobjfileupload.asp. If no, do nothing. If yes, send an AJAX HTTP POST request for http:localhost:3002uploadphoto, enclosing the file object as a whole in the body of the request message. If a JSON string containing the id and url of the uploaded photo is received, display the new photo as the last photo on the page, together with a Delete button. If an error message is received, show the error message in an alert box.
8. When handling a click event on a Delete button on the page view in Fig. 3, a confirmation box will be popped up, showing the message Are you sure you want to delete this photo?. If Cancel is clicked, the box disappears and the previous page view remains; if OK is clicked, send an AJAX HTTP DELETE request for http:localhost:3002deletephotophotoid, where the photoid should be id of the photo in the photoList collection in the database. Upon receiving an empty string in the response, the photo together with the liked message and the Delete button should be removed from the page, and the following photo entries should be moved forward to occupy its space, if it is not the last photo. If an error message is received, show the error message in an alert box.
9. When handling a click event on a Delete button on the page view in Fig. 4, similar event handling as the above should be implemented, except for the following: upon receiving an empty string in the response, the page view in Fig. 3 should be displayed, where the photo entry corresponding to the deleted photo is removed and the following photo entries are moved forward to occupy its space if it is not the last
photo.
10. When handling a click event on a Like button either on the page view in Fig. 5 or on the page view in Fig. 6, send an AJAX HTTP PUT request for http:localhost:3002updatelikephotoid, where photoid should be id of the photo in the photoList collection in the database. Upon receiving the updated likedby array in the response, display the updated liked message underneath the photo on the page. If an error message is received, show the error message in an alert box.
11. When handling a click event on a photo on the page view in Fig. 3 or Fig. 5, display the photo together with the liked message, the Delete or the Like button, and the x in the right division Fig. 4 or Fig. 6. When x is clicked, the page view returns to Fig. 3 or Fig. 5. An AJAX HTTP GET request should be sent for http:localhost:3002getalbum0 or http:localhost:3002getalbumuserid depending on whose album entry is highlighted in the left division, and response should be handled similarly as in points 5 and 6 above, to retrieve the photo information for display.
App.css 10 marks
Style your page views nicely using CSS rules in App.css.
Other marking criteria:
5 marks Good programming style avoid redundant code, easy to understand and maintain.
You are encouraged to provide a readme.txt file to let us know more about your programs. Submission:
You should zip the following files in the indicated directory structure into a yourstudentIDa2.zip file
AlbumServiceapp.js AlbumServiceroutesalbums.js MyAppsrcApp.js MyAppsrcindex.js MyAppsrcApp.css
and submit the zip file on Moodle:
1 LoginMoodle.
2 Find Assignments in the left column and click Assignment 2.
3 Click Add submission, browse your .zip file and save it. Done.
4 You will receive an automatic confirmation email, if the submission was successful.
5 You can Edit submission to your already submitted file, but ONLY before the deadline.