Files
COS498-HW3/backend/modules/PDFDatabaseManager.js
T
2025-11-27 02:43:23 +00:00

124 lines
4.7 KiB
JavaScript

// Imports
const sqlite3 = require('sqlite3').verbose();
const fs = require('fs');
const path = require('path');
// Configuration Constants
const DB_PATH = "database/backend.db";
const SCHEMA_PATH = "database/backend.schema";
const BOOK_PATH = "../frontend/public/books";
// PDF Database Manager - Manages SQLite database for storing PDF metadata
// Also handles scanning book directory and building DB
class PDFDatabaseManager {
constructor() {
if (fs.existsSync(DB_PATH)) {
fs.unlinkSync(DB_PATH);
}
this.db = new sqlite3.Database(DB_PATH);
this.createDatabase();
this.scanAndPopulateDatabase();
}
// Scans the books folder and adds all books / chapters to the database
async scanAndPopulateDatabase() {
const bookFolders = fs.readdirSync(BOOK_PATH);
bookFolders.forEach(folder => {
const bookDir = path.join(BOOK_PATH, folder);
const bookMetadataPath = path.join(bookDir, 'bookMetadata.json');
if (fs.existsSync(bookMetadataPath)) {
const bookMetadata = JSON.parse(fs.readFileSync(bookMetadataPath, 'utf-8'));
this.addBook(bookMetadata);
const chapterFiles = fs.readdirSync(bookDir).filter(file => file.endsWith('.json') && file !== 'bookMetadata.json');
chapterFiles.forEach(async chapterFile => {
const chapterMetadataPath = path.join(bookDir, chapterFile);
const chapterMetadata = JSON.parse(fs.readFileSync(chapterMetadataPath, 'utf-8'));
chapterMetadata.book_id = await this.getBookIdByFolderName(folder);
this.addChapter(chapterMetadata);
});
}
});
}
// Adds chapter metadata from FS to db
addChapter(chapterMetadata) {
let chapterQuery = `INSERT INTO chapters (chapter_number, display_name, filename, book_id) VALUES (?, ?, ?, ?)`;
this.db.run(chapterQuery, [chapterMetadata.chapter_number, chapterMetadata.display_name, chapterMetadata.filename, chapterMetadata.book_id]);
}
// Adds book metadata from FS to db
addBook(bookMetadata) {
let bookQuery = `INSERT INTO books (folder_name, display_name, author) VALUES (?, ?, ?)`;
this.db.run(bookQuery, [bookMetadata.folder_name, bookMetadata.display_name, bookMetadata.author]);
}
// Retrieves book ID by folder name
getBookIdByFolderName(folderName) {
return new Promise((resolve) => {
let query = `SELECT id FROM books WHERE folder_name = ?`;
this.db.get(query, [folderName], (err, row) => {
if (err) {
resolve(null);
} else {
resolve(row ? row.id : null);
}
});
});
}
// Creates the database if it doesn't exist
createDatabase() {
// Load DB schema from file and initialize database
const schema = fs.readFileSync(SCHEMA_PATH, 'utf-8');
this.db.exec(schema, (err) => {
if (err) {
console.error('Error creating database schema:', err.message);
} else {
console.log('Database schema created successfully.');
}
});
}
// List all books in the database
loadBooks() {
return new Promise((resolve, reject) => {
const query = 'SELECT id, folder_name, display_name, author FROM books ORDER BY display_name';
this.db.all(query, [], (err, rows) => {
const books = rows.map(row => ({
id: row.id,
name: row.folder_name,
displayName: row.display_name,
author: row.author
}));
resolve(books);
});
});
}
// List all chapters for a specific book
loadChapters(bookName) {
return new Promise((resolve, reject) => {
const query = `
SELECT c.id, c.chapter_number, c.display_name, c.filename, b.folder_name
FROM chapters c
JOIN books b ON c.book_id = b.id
WHERE b.folder_name = ?
ORDER BY c.chapter_number
`;
this.db.all(query, [bookName], (err, rows) => {
const chapters = rows.map(row => ({
id: row.id,
chapterNumber: row.chapter_number,
filename: row.filename,
displayName: row.display_name,
url: `/pdf/${row.folder_name}/${row.filename}`
}));
resolve(chapters);
});
});
}
}
module.exports = PDFDatabaseManager;