const axios = require('axios'); const express = require('express'); const https = require('https'); const fs = require('fs'); const path = require('path'); const sqlite3 = require('sqlite3').verbose(); const bodyParser = require('body-parser'); const app = express(); const PORT = process.env.PORT || 3000; // SSL certificate paths const privateKey = fs.readFileSync(path.join(__dirname, 'server.key'), 'utf8'); const certificate = fs.readFileSync(path.join(__dirname, 'server.cert'), 'utf8'); const credentials = { key: privateKey, cert: certificate }; // Middleware to parse JSON bodies app.use(express.json()); // Use built-in body-parser for JSON // Set up the SQLite database // Set up the SQLite database const db = new sqlite3.Database('./books.db', (err) => { if (err) { console.error('Could not connect to database', err); } else { console.log('Connected to database'); db.run(` CREATE TABLE IF NOT EXISTS books ( id INTEGER PRIMARY KEY AUTOINCREMENT, isbn TEXT, title TEXT, authors TEXT, publishedDate TEXT, description TEXT, url TEXT, number_of_pages INTEGER, identifiers TEXT, publishers TEXT, subjects TEXT, notes TEXT, cover_small TEXT, cover_medium TEXT, cover_large TEXT ) `); } }); // Serve static files from the 'public' directory app.use(express.static(path.join(__dirname, 'public'))); // Endpoint to fetch book details by ISBN // Endpoint to fetch book details by ISBN app.get('/book/:isbn', async (req, res) => { const { isbn } = req.params; console.log(`Fetching book data for ISBN: ${isbn}`); try { // First try Open Library const openLibraryResponse = await axios.get(`https://openlibrary.org/api/books?bibkeys=ISBN:${isbn}&format=json&jscmd=data`); const bookData = openLibraryResponse.data[`ISBN:${isbn}`]; //Check for timeout or rate limit: if(openLibraryResponse.status === 429){ console.log('Rate limit exceeded'); res.status(429).json({ error: 'Rate limit exceeded' }); } if(openLibraryResponse.status === 408){ console.log('Request Timeout'); res.status(408).json({ error: 'Request Timeout' }); } if (bookData) { console.log('Book data found in Open Library'); res.json(formatOpenLibraryData(bookData)); } else { console.log('Book not found in Open Library'); console.log('Trying Internet Archive...'); // Fallback to Internet Archive if no data from Open Library const archiveResponse = await axios.get(`https://archive.org/advancedsearch.php?q=isbn:${isbn}&output=json`); const archiveData = archiveResponse.data; console.log(archiveData); if (archiveData.response.numFound > 0) { res.json(formatArchiveData(archiveData.response.docs[0])); } else { res.status(404).json({ error: 'Book not found in Open Library or Internet Archive.' }); } } } catch (error) { console.error(error); res.status(500).json({ error: 'Failed to fetch book data' }); } }); // Endpoint to search for book by title app.get('/search-title', async (req, res) => { const { title } = req.query; console.log(`Searching for book by title: ${title}`); try { // Search Open Library by title, limit to 5 results const openLibraryResponse = await axios.get(`https://openlibrary.org/search.json?q=${encodeURIComponent(title)}&limit=7`); const searchResults = openLibraryResponse.data.docs; console.log(searchResults); if (searchResults.length > 0) { console.log('Books found by title'); const bookData = searchResults.map(result => ({ title: result.title, authors: result.author_name || [], publish_date: result.first_publish_year, isbn: result.isbn ? result.isbn[0] : '', publisher: result.publisher ? result.publisher[0] : '', key: result.key // Unique key to fetch more detailed data later if needed })); console.log(bookData); res.json({ results: bookData }); } else { res.status(404).json({ error: 'No books found with that title.' }); } } catch (error) { console.error(error); res.status(500).json({ error: 'Failed to search for book by title' }); } }); // Endpoint to store book in the database // Endpoint to store book in the database // Endpoint to store book in the database app.post('/store-book', (req, res) => { const { isbn, title, authors, publishedDate, description, url, number_of_pages, identifiers, publishers, subjects, notes, cover_small, cover_medium, cover_large } = req.body; // Check if a book with the same ISBN already exists const checkQuery = `SELECT * FROM books WHERE isbn = ?`; db.get(checkQuery, [isbn], (err, row) => { if (err) { console.error('Error checking for existing book:', err); return res.status(500).json({ error: 'Failed to check for existing book' }); } if (row) { // Book already exists, update the existing record const updateQuery = ` UPDATE books SET title = ?, authors = ?, publishedDate = ?, description = ?, url = ?, number_of_pages = ?, identifiers = ?, publishers = ?, subjects = ?, notes = ?, cover_small = ?, cover_medium = ?, cover_large = ? WHERE isbn = ? `; const updateParams = [ title, authors ? authors.join(', ') : '', publishedDate, description || '', url, number_of_pages, identifiers, publishers, subjects, notes, cover_small, cover_medium, cover_large, isbn ]; db.run(updateQuery, updateParams, function (err) { if (err) { console.error('Error updating book in database:', err); return res.status(500).json({ error: 'Failed to update book in database' }); } else { res.json({ success: true, message: 'Book updated successfully' }); } }); } else { // Book does not exist, insert a new record const insertQuery = ` INSERT INTO books ( isbn, title, authors, publishedDate, description, url, number_of_pages, identifiers, publishers, subjects, notes, cover_small, cover_medium, cover_large ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `; const insertParams = [ isbn, title, authors ? authors.join(', ') : '', publishedDate, description || '', url, number_of_pages, identifiers, publishers, subjects, notes, cover_small, cover_medium, cover_large ]; db.run(insertQuery, insertParams, function (err) { if (err) { console.error('Error storing book in database:', err); return res.status(500).json({ error: 'Failed to store book in database' }); } else { res.json({ success: true, message: 'Book stored successfully', bookId: this.lastID }); } }); } }); }); function formatOpenLibraryData(data) { return { isbn: data.identifiers.isbn_13 ? data.identifiers.isbn_13[0] : '', title: data.title, authors: data.authors ? data.authors.map(author => author.name) : [], publishedDate: data.publish_date, description: data.excerpts ? data.excerpts[0].text : 'No description available', url: data.url, number_of_pages: data.number_of_pages || null, identifiers: JSON.stringify(data.identifiers), // Store as JSON string publishers: data.publishers ? data.publishers.map(pub => pub.name).join(', ') : '', subjects: data.subjects ? data.subjects.map(sub => sub.name).join(', ') : '', notes: data.notes || '', cover_small: data.cover ? data.cover.small : '', cover_medium: data.cover ? data.cover.medium : '', cover_large: data.cover ? data.cover.large : '' }; } function formatArchiveData(data) { return { isbn: data.isbn ? data.isbn[0] : '', title: data.title, authors: data.contributor ? [data.contributor] : [], publishedDate: data.date ? new Date(data.date).getFullYear() : '', description: data.description ? data.description.join(' ') : 'No description available', url: `https://archive.org/details/${data.identifier}`, number_of_pages: data.imagecount || null, identifiers: JSON.stringify({ archive_identifier: data.identifier, oclc: data['external-identifier'] ? data['external-identifier'].filter(id => id.includes('urn:oclc')).map(id => id.split(':')[2]) : [] }), publishers: data.publisher || '', subjects: data.subject ? data.subject.join(', ') : '', notes: '', // Archive data does not provide specific notes like Open Library cover_small: '', // Placeholder, as cover image URLs need to be constructed manually cover_medium: '', // Same as above cover_large: '' // Same as above }; } const httpsServer = https.createServer(credentials, app); httpsServer.listen(PORT, () => { console.log(`HTTPS Server running on https://localhost:${PORT}`); });