Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,31 @@ To get this project up and running locally on your computer:
```bash
npm install
```

3. Run the tutorial server, using the appropriate command line shell for your environment:

```bash
# Linux terminal
DEBUG=express-locallibrary-tutorial:* npm run devstart

# Windows Powershell
$ENV:DEBUG = "express-locallibrary-tutorial:*"; npm start
```

4. Open a browser to <http://localhost:3000/> to open the library site.

> **Note:** The library uses a default MongoDB database hosted on [MongoDB Atlas](https://www.mongodb.com/cloud/atlas). You should use a different database for your own code experiments.
> [!NOTE]
> The library uses a default MongoDB database hosted on [MongoDB Atlas](https://www.mongodb.com/cloud/atlas).
> You should use a different database for your own code experiments.

## Contributing

The project is the result of carefully running through all the steps in the [tutorial on MDN](https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website).
Any changes to either MDN or this project must be synchronized.
Generally it is better to implement changes here before submitting them to MDN.

Before submitting a PR, make sure that the tests pass:

```bash
npm test
```
2 changes: 2 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const path = require("path");
const cookieParser = require("cookie-parser");
const logger = require("morgan");
const RateLimit = require("express-rate-limit");
const methodOverride = require("method-override");

const indexRouter = require("./routes/index");
const usersRouter = require("./routes/users");
Expand All @@ -30,6 +31,7 @@ app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(methodOverride("_method"));

app.use(
helmet.contentSecurityPolicy({
Expand Down
32 changes: 31 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"helmet": "^8.1.0",
"http-errors": "^2.0.0",
"luxon": "^3.6.1",
"method-override": "^3.0.0",
"mongoose": "^8.16.2",
"morgan": "^1.10.0",
"pug": "^3.0.3"
Expand Down
28 changes: 14 additions & 14 deletions routes/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ router.post("/book/create", book_controller.book_create_post);
// GET request to delete Book.
router.get("/book/:id/delete", book_controller.book_delete_get);

// POST request to delete Book.
router.post("/book/:id/delete", book_controller.book_delete_post);
// DELETE request to delete Book.
router.delete("/book/:id/delete", book_controller.book_delete_post);

// GET request to update Book.
router.get("/book/:id/update", book_controller.book_update_get);
Expand All @@ -48,8 +48,8 @@ router.post("/author/create", author_controller.author_create_post);
// GET request to delete Author.
router.get("/author/:id/delete", author_controller.author_delete_get);

// POST request to delete Author
router.post("/author/:id/delete", author_controller.author_delete_post);
// DELETE request to delete Author
router.delete("/author/:id/delete", author_controller.author_delete_post);

// GET request to update Author.
router.get("/author/:id/update", author_controller.author_update_get);
Expand All @@ -74,8 +74,8 @@ router.post("/genre/create", genre_controller.genre_create_post);
// GET request to delete Genre.
router.get("/genre/:id/delete", genre_controller.genre_delete_get);

// POST request to delete Genre.
router.post("/genre/:id/delete", genre_controller.genre_delete_post);
// DELETE request to delete Genre.
router.delete("/genre/:id/delete", genre_controller.genre_delete_post);

// GET request to update Genre.
router.get("/genre/:id/update", genre_controller.genre_update_get);
Expand All @@ -94,37 +94,37 @@ router.get("/genres", genre_controller.genre_list);
// GET request for creating a BookInstance. NOTE This must come before route that displays BookInstance (uses id).
router.get(
"/bookinstance/create",
book_instance_controller.bookinstance_create_get
book_instance_controller.bookinstance_create_get,
);

// POST request for creating BookInstance.
router.post(
"/bookinstance/create",
book_instance_controller.bookinstance_create_post
book_instance_controller.bookinstance_create_post,
);

// GET request to delete BookInstance.
router.get(
"/bookinstance/:id/delete",
book_instance_controller.bookinstance_delete_get
book_instance_controller.bookinstance_delete_get,
);

// POST request to delete BookInstance.
router.post(
// DELETE request to delete BookInstance.
router.delete(
"/bookinstance/:id/delete",
book_instance_controller.bookinstance_delete_post
book_instance_controller.bookinstance_delete_post,
);

// GET request to update BookInstance.
router.get(
"/bookinstance/:id/update",
book_instance_controller.bookinstance_update_get
book_instance_controller.bookinstance_update_get,
);

// POST request to update BookInstance.
router.post(
"/bookinstance/:id/update",
book_instance_controller.bookinstance_update_post
book_instance_controller.bookinstance_update_post,
);

// GET request for one BookInstance.
Expand Down
4 changes: 2 additions & 2 deletions test/routes/author.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ test("Author Routes", async (t) => {
"should delete the author and redirect to author list",
async () => {
const res = await request(app)
.post(`/catalog/author/${author._id}/delete`)
.delete(`/catalog/author/${author._id}/delete`)
.type("form")
.send({ authorid: author._id.toString() }) // Form body
.expect(302);
Expand Down Expand Up @@ -147,7 +147,7 @@ test("Author Routes", async (t) => {
});

const res = await request(app)
.post(`/catalog/author/${author._id}/delete`)
.delete(`/catalog/author/${author._id}/delete`)
.type("form")
.send({ id: author._id.toString() }) // Note: Was `id`, but controller likely expects `authorid` for consistency
.expect(200); // Should NOT redirect
Expand Down
4 changes: 2 additions & 2 deletions test/routes/book.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ test("Book Routes", async (t) => {
"should delete the book and redirect to book list",
async () => {
const res = await request(app)
.post(`/catalog/book/${book._id}/delete`)
.delete(`/catalog/book/${book._id}/delete`)
.type("form") // Simulates form POST
.send({ id: book._id.toString() }) // This is where server gets the ID
.expect(302);
Expand Down Expand Up @@ -166,7 +166,7 @@ test("Book Routes", async (t) => {
});

const res = await request(app)
.post(`/catalog/book/${book._id}/delete`)
.delete(`/catalog/book/${book._id}/delete`)
.type("form")
.send({ id: book._id.toString() })
.expect(200);
Expand Down
2 changes: 1 addition & 1 deletion test/routes/bookinstance.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ test("BookInstance Routes", async (t) => {
"should delete the BookInstance and redirect to BookInstance list",
async () => {
const res = await request(app)
.post(`/catalog/bookinstance/${bookInstance._id}/delete`)
.delete(`/catalog/bookinstance/${bookInstance._id}/delete`)
.type("form")
.send({ id: bookInstance._id.toString() })
.expect(302);
Expand Down
4 changes: 2 additions & 2 deletions test/routes/genre.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ test("Genre Routes", async (t) => {
"should delete the genre and redirect to genre list",
async () => {
const res = await request(app)
.post(`/catalog/genre/${genre._id}/delete`)
.delete(`/catalog/genre/${genre._id}/delete`)
.type("form")
.send({ id: genre._id.toString() })
.expect(302);
Expand Down Expand Up @@ -134,7 +134,7 @@ test("Genre Routes", async (t) => {
});

const res = await request(app)
.post(`/catalog/genre/${genre._id}/delete`)
.delete(`/catalog/genre/${genre._id}/delete`)
.type("form")
.send({ id: genre._id.toString() })
.expect(200); // Should NOT redirect, but render page with error
Expand Down
4 changes: 2 additions & 2 deletions views/author_delete.pug
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ block content
else
p Do you really want to delete this Author?

form(method='POST')
form(method='POST' action='?_method=DELETE')
div.form-group
input#authorid.form-control(type='hidden', name='authorid', value=author._id )

button.btn.btn-primary(type='submit') Delete
button.btn.btn-primary(type='submit') Delete
2 changes: 1 addition & 1 deletion views/book_delete.pug
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ block content
else
p Do you really want to delete this Book?

form(method='POST')
form(method='POST' action='?_method=DELETE')
div.form-group
input#id.form-control(type='hidden',name='id', value=book._id )

Expand Down
4 changes: 2 additions & 2 deletions views/bookinstance_delete.pug
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ block content
if bookinstance.status!='Available'
p #[strong Due back:] #{bookinstance.due_back_formatted}

form(method='POST')
form(method='POST' action='?_method=DELETE')
div.form-group
input#id.form-control(type='hidden',name='id', value=bookinstance._id )

button.btn.btn-primary(type='submit') Delete
button.btn.btn-primary(type='submit') Delete
4 changes: 2 additions & 2 deletions views/genre_delete.pug
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ block content
else
p Do you really want to delete this Genre?

form(method='POST')
form(method='POST' action='?_method=DELETE')
div.form-group
input#id.form-control(type='hidden', name='id', value=genre._id )

button.btn.btn-primary(type='submit') Delete
button.btn.btn-primary(type='submit') Delete