diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d59079e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +# Use an official Node.js runtime as a parent image +FROM node:14 + +# Set the working directory in the container +WORKDIR /usr/src/app + +# Copy package.json and package-lock.json to the working directory +COPY package*.json ./ + +# Install any needed packages specified in package.json +RUN npm install + +# Copy the rest of the application code to the working directory +COPY . . + +# Expose the port the app runs on +EXPOSE 3000 + +# Define environment variable +ENV NODE_ENV=production + +# Run the application +CMD ["node", "index.js"] diff --git a/books.db b/books.db index 7c7a199..e9e4c69 100644 Binary files a/books.db and b/books.db differ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..174347a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,20 @@ +version: '3.8' + +services: + library-manager: + build: + context: . + dockerfile: Dockerfile + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - GOOGLE_BOOKS_API_KEY=${GOOGLE_BOOKS_API_KEY} + - ADMIN_EMAIL=${ADMIN_EMAIL} + - EMAIL_PASSWORD=${EMAIL_PASSWORD} + - DOMAIN=${DOMAIN} + volumes: + - .:/usr/src/app + - ./data:/usr/src/app/data + + diff --git a/index.js b/index.js index df0a813..3bad1e9 100644 --- a/index.js +++ b/index.js @@ -7,6 +7,7 @@ const sqlite3 = require('sqlite3').verbose(); const bodyParser = require('body-parser'); const nodemailer = require('nodemailer'); // Add nodemailer for sending emails const rateLimit = require('express-rate-limit'); // Ensure this line is present +const basicAuth = require('express-basic-auth'); // Add express-basic-auth const library = require('./library'); const { @@ -38,6 +39,21 @@ app.use(express.json()); // Use built-in body-parser for JSON // Serve static files from the 'public' directory app.use(express.static(path.join(__dirname, 'public'))); +// Basic Auth middleware +const authMiddleware = basicAuth({ + users: { 'admin': process.env.ADMIN_PASSWORD }, // Use environment variable for password + challenge: true, + unauthorizedResponse: (req) => 'Unauthorized' +}); + +// Apply auth middleware to all non-GET requests +app.use((req, res, next) => { + if (req.method !== 'GET') { + return authMiddleware(req, res, next); + } + next(); +}); + app.get('/book/:isbn', async (req, res) => { const { isbn } = req.params; console.log(`Fetching book data for ISBN: ${isbn}`); diff --git a/libraryManager.py b/libraryManager.py index 918e9ee..205dc0d 100644 --- a/libraryManager.py +++ b/libraryManager.py @@ -3,10 +3,15 @@ from rich.console import Console from rich.prompt import Prompt from rich.table import Table from fuzzywuzzy import process +from requests.auth import HTTPBasicAuth API_BASE_URL = "https://localhost:3000" # Replace with your actual API base URL console = Console() +# Use environment variables or a secure method to store credentials +USERNAME = 'admin' +PASSWORD = 'library@123' # Replace with your actual password + def list_books(): response = requests.get(f"{API_BASE_URL}/api/books-with-images", verify=False) if response.status_code == 200: @@ -30,7 +35,12 @@ def add_book(): "title": title, "authors": authors } - response = requests.post(f"{API_BASE_URL}/store-book", json=data, verify=False) + response = requests.post( + f"{API_BASE_URL}/store-book", + json=data, + verify=False, + auth=HTTPBasicAuth(USERNAME, PASSWORD) + ) if response.status_code == 200: console.print("Book added successfully.", style="bold green") else: @@ -38,7 +48,11 @@ def add_book(): def remove_book(): isbn = Prompt.ask("Enter ISBN of the book to remove") - response = requests.delete(f"{API_BASE_URL}/book/{isbn}", verify=False) + response = requests.delete( + f"{API_BASE_URL}/book/{isbn}", + verify=False, + auth=HTTPBasicAuth(USERNAME, PASSWORD) + ) if response.status_code == 200: console.print("Book removed successfully.", style="bold green") else: @@ -48,7 +62,12 @@ def change_book_status(): isbn = Prompt.ask("Enter ISBN of the book to change status") status = Prompt.ask("Enter new status (e.g., Available, Checked Out)") data = {"status": status} - response = requests.put(f"{API_BASE_URL}/book/{isbn}", json=data, verify=False) + response = requests.put( + f"{API_BASE_URL}/book/{isbn}", + json=data, + verify=False, + auth=HTTPBasicAuth(USERNAME, PASSWORD) + ) if response.status_code == 200: console.print("Book status updated successfully.", style="bold green") else: diff --git a/package-lock.json b/package-lock.json index 6df61fa..29885ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "axios": "^1.7.5", "dotenv": "^16.4.5", "express": "^4.19.2", + "express-basic-auth": "^1.2.1", "express-rate-limit": "^7.4.1", "nodemailer": "^6.9.16", "sequelize": "^6.37.3", @@ -275,6 +276,24 @@ ], "license": "MIT" }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -851,6 +870,15 @@ "url": "https://opencollective.com/express" } }, + "node_modules/express-basic-auth": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/express-basic-auth/-/express-basic-auth-1.2.1.tgz", + "integrity": "sha512-L6YQ1wQ/mNjVLAmK3AG1RK6VkokA1BIY6wmiH304Xtt/cLTps40EusZsU1Uop+v9lTDPxdtzbFmdXfFO3KEnwA==", + "license": "MIT", + "dependencies": { + "basic-auth": "^2.0.1" + } + }, "node_modules/express-rate-limit": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.4.1.tgz", diff --git a/package.json b/package.json index 32b96c6..be4833d 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "axios": "^1.7.5", "dotenv": "^16.4.5", "express": "^4.19.2", + "express-basic-auth": "^1.2.1", "express-rate-limit": "^7.4.1", "nodemailer": "^6.9.16", "sequelize": "^6.37.3", diff --git a/public/index.html b/public/index.html index 42397bb..0e7776b 100644 --- a/public/index.html +++ b/public/index.html @@ -3,34 +3,145 @@ - ISBN Scanner and Title Lookup + Ramsey Library -

Scan a Book ISBN or Search by Title

-
- - - - - s -
-
-
-

Or search for a book by title

- - -
-
-
-

-

-

-

- - -
- - +

Ramsey Library

+ + + + + + + + + + + + + + + + + +
TitleAuthorsPublisherPublished DateISBNStatusCheckout
+ + diff --git a/public/library.html b/public/library.html deleted file mode 100644 index 0e7776b..0000000 --- a/public/library.html +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - Ramsey Library - - - -

Ramsey Library

- - - - - - - - - - - - - - - - - -
TitleAuthorsPublisherPublished DateISBNStatusCheckout
- - - - diff --git a/public/scanner.html b/public/scanner.html new file mode 100644 index 0000000..42397bb --- /dev/null +++ b/public/scanner.html @@ -0,0 +1,36 @@ + + + + + + ISBN Scanner and Title Lookup + + + +

Scan a Book ISBN or Search by Title

+
+ + + + + s +
+
+
+

Or search for a book by title

+ + +
+
+
+

+

+

+

+ + +
+ + + +