feat: add book metadata editor with Google Books and OpenLibrary integration
This commit is contained in:
183
index.js
183
index.js
@@ -130,6 +130,16 @@ app.get('/api/books-with-images', async (req, res) => {
|
||||
},
|
||||
attributes: ['isbn', 'title', 'cover_small', 'cover_medium', 'cover_large', 'authors'],
|
||||
});
|
||||
// CORRECTED DEBUG LOGGING ON SERVER
|
||||
if (books && books.length > 0) {
|
||||
// Log a specific book if its ID is known, e.g., ID 207 for Artemis Fowl
|
||||
const specificBookForDebug = books.find(b => b.id === 207);
|
||||
if (specificBookForDebug) {
|
||||
console.log("Server-side specific book from /api/books (ID 207):", JSON.stringify(specificBookForDebug, null, 2));
|
||||
} else {
|
||||
console.log("Server-side book ID 207 not found in /api/books results, logging first book instead:", JSON.stringify(books[0], null, 2));
|
||||
}
|
||||
}
|
||||
res.json(books);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch books with images:', error);
|
||||
@@ -301,23 +311,25 @@ function delay(ms) {
|
||||
// Confirm book is in the library by ISBN
|
||||
app.get('/book/confirm/:isbn', async (req, res) => {
|
||||
const { isbn } = req.params;
|
||||
console.log(`Fetching book data for ISBN: ${isbn}`);
|
||||
console.log(`Cascade: /book/confirm/:isbn - Confirming ISBN: ${isbn}`);
|
||||
|
||||
try {
|
||||
// Check if the book is in the local database
|
||||
const localBook = await fetchBookFromLocalDatabase(isbn);
|
||||
|
||||
if (localBook) {
|
||||
console.log('Book found in the local database');
|
||||
console.log('Cascade: /book/confirm/:isbn - Book found in local database.');
|
||||
return res.json({ source: 'local', data: localBook });
|
||||
} else {
|
||||
console.log('Cascade: /book/confirm/:isbn - Book NOT found in local database.');
|
||||
return res.status(404).json({ error: 'Book not found in local library' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.status(500).json({ error: 'Failed to fetch book data' });
|
||||
console.error('Cascade: /book/confirm/:isbn - Error during lookup:', error);
|
||||
return res.status(500).json({ error: 'Failed to confirm book in local library' });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
app.get('/search-title', async (req, res) => {
|
||||
const { title, internalOnly = false } = req.query;
|
||||
console.log(`Searching for books by title or related fields: ${title}`);
|
||||
@@ -393,9 +405,14 @@ app.delete('/book/:id', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/locations', async (req, res) => {
|
||||
app.get('/api/locations', async (req, res) => {
|
||||
try {
|
||||
const locations = await Location.findAll();
|
||||
const locations = await Location.findAll({
|
||||
order: [
|
||||
['name', 'ASC'],
|
||||
['shelf', 'ASC']
|
||||
]
|
||||
});
|
||||
res.json(locations);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch locations:', error);
|
||||
@@ -657,6 +674,156 @@ function requestCheckout(isbn) {
|
||||
});
|
||||
}
|
||||
|
||||
// API endpoint to get all books
|
||||
app.get('/api/books', async (req, res) => {
|
||||
try {
|
||||
const books = await Book.findAll({
|
||||
include: [{
|
||||
model: Location,
|
||||
as: 'Location' // Corrected alias based on model definition
|
||||
}],
|
||||
order: [['title', 'ASC']]
|
||||
});
|
||||
res.json(books.map(book => ({
|
||||
id: book.id,
|
||||
title: book.title,
|
||||
authors: book.authors,
|
||||
isbn: book.isbn,
|
||||
publishedDate: book.publishedDate,
|
||||
description: book.description,
|
||||
cover_small: book.cover_small,
|
||||
number_of_pages: book.number_of_pages,
|
||||
publishers: book.publishers,
|
||||
subjects: book.subjects,
|
||||
location_id: book.location_id,
|
||||
Location: book.Location ? { id: book.Location.id, name: book.Location.name, room: book.Location.room, shelf: book.Location.shelf } : null
|
||||
})));
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch books:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch books' });
|
||||
}
|
||||
});
|
||||
|
||||
// API endpoint to add a new book
|
||||
app.post('/api/books', async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
title,
|
||||
authors,
|
||||
publishedDate,
|
||||
description,
|
||||
isbn,
|
||||
number_of_pages,
|
||||
publishers,
|
||||
subjects,
|
||||
cover_small,
|
||||
cover_medium,
|
||||
cover_large,
|
||||
location_id
|
||||
} = req.body;
|
||||
|
||||
// Basic validation (e.g., title is required)
|
||||
if (!title) {
|
||||
return res.status(400).json({ error: 'Title is required' });
|
||||
}
|
||||
|
||||
const newBookData = {
|
||||
title,
|
||||
authors: Array.isArray(authors) ? authors.join(', ') : authors,
|
||||
publishedDate,
|
||||
description,
|
||||
isbn,
|
||||
number_of_pages,
|
||||
publishers: Array.isArray(publishers) ? publishers.join(', ') : publishers,
|
||||
subjects: Array.isArray(subjects) ? subjects.join(', ') : subjects,
|
||||
cover_small,
|
||||
cover_medium,
|
||||
cover_large,
|
||||
location_id: location_id === '' ? null : location_id // Handle empty string as null
|
||||
};
|
||||
|
||||
const book = await Book.create(newBookData);
|
||||
|
||||
// Fetch the newly created book with its location to send back in the response
|
||||
const createdBookWithLocation = await Book.findByPk(book.id, {
|
||||
include: [{
|
||||
model: Location,
|
||||
as: 'Location'
|
||||
}]
|
||||
});
|
||||
|
||||
res.status(201).json({ success: true, book: createdBookWithLocation });
|
||||
} catch (error) {
|
||||
console.error('Failed to add new book:', error);
|
||||
if (error.name === 'SequelizeUniqueConstraintError') {
|
||||
let errorMessage = 'Failed to add new book due to a conflict.';
|
||||
// Assuming 'isbn' is a unique field in your Book model
|
||||
if (error.fields && typeof error.fields === 'object' && 'isbn' in error.fields) {
|
||||
errorMessage = 'A book with this ISBN already exists. Please use the edit feature if you want to update it.';
|
||||
}
|
||||
return res.status(409).json({ error: errorMessage });
|
||||
}
|
||||
res.status(500).json({ error: 'Failed to add new book' });
|
||||
}
|
||||
});
|
||||
|
||||
// API endpoint to update a book's metadata by its primary key ID
|
||||
app.put('/api/books/:id', async (req, res) => {
|
||||
try {
|
||||
const bookId = req.params.id;
|
||||
const book = await Book.findByPk(bookId);
|
||||
|
||||
if (!book) {
|
||||
return res.status(404).json({ error: 'Book not found' });
|
||||
}
|
||||
|
||||
const {
|
||||
title,
|
||||
authors,
|
||||
publishedDate,
|
||||
description,
|
||||
isbn,
|
||||
number_of_pages,
|
||||
publishers,
|
||||
subjects,
|
||||
cover_small,
|
||||
cover_medium,
|
||||
cover_large,
|
||||
location_id // Added to handle location updates
|
||||
} = req.body;
|
||||
|
||||
const updateData = {};
|
||||
if (title !== undefined) updateData.title = title;
|
||||
if (authors !== undefined) updateData.authors = Array.isArray(authors) ? authors.join(', ') : authors;
|
||||
if (publishedDate !== undefined) updateData.publishedDate = publishedDate;
|
||||
if (description !== undefined) updateData.description = description;
|
||||
if (isbn !== undefined) updateData.isbn = isbn === '' ? null : isbn;
|
||||
if (number_of_pages !== undefined) updateData.number_of_pages = number_of_pages;
|
||||
if (publishers !== undefined) updateData.publishers = Array.isArray(publishers) ? publishers.join(', ') : publishers;
|
||||
if (subjects !== undefined) updateData.subjects = Array.isArray(subjects) ? subjects.join(', ') : subjects;
|
||||
if (cover_small !== undefined) updateData.cover_small = cover_small;
|
||||
if (cover_medium !== undefined) updateData.cover_medium = cover_medium;
|
||||
if (cover_large !== undefined) updateData.cover_large = cover_large;
|
||||
if (location_id !== undefined) updateData.location_id = location_id === '' ? null : location_id; // Handle empty string as null
|
||||
|
||||
await book.update(updateData);
|
||||
console.log(`Book data for ID ${bookId} updated successfully.`);
|
||||
|
||||
// Fetch the updated book with its location to send back in the response
|
||||
const updatedBookWithLocation = await Book.findByPk(bookId, {
|
||||
include: [{
|
||||
model: Location,
|
||||
as: 'Location'
|
||||
}]
|
||||
});
|
||||
res.json({ success: true, book: updatedBookWithLocation });
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Failed to update book with ID ${req.params.id}:`, error);
|
||||
res.status(500).json({ error: 'Failed to update book' });
|
||||
}
|
||||
});
|
||||
|
||||
const httpsServer = https.createServer(credentials, app);
|
||||
|
||||
|
||||
@@ -698,7 +865,7 @@ app.get('/api/books-on-loan', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/book/update/:isbn', async (req, res) => {
|
||||
app.post('/book/update/:isbn', authMiddleware, async (req, res) => { // Added authMiddleware for consistency
|
||||
const { isbn } = req.params;
|
||||
const googleBooksApiKey = process.env.GOOGLE_BOOKS_API_KEY;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user