diff --git a/src/books/book-service.ts b/src/books/book-service.ts index 87bbce8..d4ed108 100644 --- a/src/books/book-service.ts +++ b/src/books/book-service.ts @@ -1,31 +1,74 @@ +import { Database } from "../../database.ts"; import { dustService } from "../../main.ts"; +import { Author, AuthorWithId } from "./author.ts"; import { BookCrawler } from "./book-crawler.ts"; -import type { Book } from "./book.ts"; -import { addAuthorIfNotExists, addBookIfNotExists } from "./data.ts"; +import type { Book, BookWithId } from "./book.ts"; +import { + addAuthorIfNotExists, + addBookIfNotExists, + getAllAuthors, + getAllBooks, + getAuthorById, + getAuthorByName, + getBook, +} from "./data.ts"; import { FSWalker } from "./fs/fs-walker.ts"; export class BookService { + async populateBooksDB(dirs: Array) { + const fsWalker = new FSWalker(dirs, { supportedFiletypes: ["pdf"] }); + const crawler = new BookCrawler(fsWalker); + const books = await crawler.crawlForBooks(); + const groupedByAuthor = books.reduce((acc, cur) => { + if (!acc.get(cur.author)) { + acc.set(cur.author, []); + } + acc.get(cur.author)?.push(cur); + return acc; + }, new Map>>()); - async populateBooksDB(dirs: Array) { - const fsWalker = new FSWalker(dirs, {supportedFiletypes: ['pdf']}); - const crawler = new BookCrawler(fsWalker); - const books = await crawler.crawlForBooks(); - const groupedByAuthor = books.reduce((acc, cur) => { - if (!acc.get(cur.author)) { - acc.set(cur.author, []) - } - acc.get(cur.author)?.push(cur); - return acc; - }, new Map>>()); + for (const [author, books] of groupedByAuthor.entries()) { + const _author = await addAuthorIfNotExists(dustService.database, author); + for (const book of books) { + await addBookIfNotExists(dustService.database, { + name: book.name, + filepath: book.filepath, + author: _author.id, + }); + } + } + } + + async getBookById( + database: Database, + id: string + ): Promise & { author: Author }> { + const book = await getBook(database, id); + const author = await getAuthorById(database, book.author); - for (const [author, books] of groupedByAuthor.entries()) { - const _author = await addAuthorIfNotExists(dustService.database, author); - for (const book of books) { - await addBookIfNotExists(dustService.database, {name: book.name, filepath: book.filepath, author: _author.id}); - } - } + return { + ...book, + author: author, + }; + } + async getBooks( + database: Database + ): Promise<(Omit & { author: Author | undefined })[]> { + const books = await getAllBooks(database); + const authors = await getAllAuthors(database); + const authorsById = new Map(); + for (const author of authors) { + authorsById.set(author.id, author); } + + return books.map((book) => { + return { + ...book, + author: authorsById.get(book.author), + }; + }); + } } -export const bookService: BookService = new BookService(); \ No newline at end of file +export const bookService: BookService = new BookService(); diff --git a/src/books/data.ts b/src/books/data.ts index ac46ee4..23ced33 100644 --- a/src/books/data.ts +++ b/src/books/data.ts @@ -1,6 +1,6 @@ import type { Database } from "../../database.ts"; import type { AuthorWithId } from "./author.ts"; -import type { Book } from "./book.ts"; +import type { Book, BookWithId } from "./book.ts"; export const migrate = (database: Database) => { return database.migrate([ @@ -15,17 +15,33 @@ export const migrate = (database: Database) => { ]); } -export const getAllBooks = (database: Database) => { - return database.execute(` +export const getAllBooks = async (database: Database): Promise => { + const resp = await database.execute(` SELECT rowid, * FROM books; `); + + return resp.rows.map((r) => { + return { + id: r['rowid'] as number, + name: r['name'] as string, + filepath: r['file_path'] as string, + author: r['author'] as number, + }; + }) } -export const getBook = (database: Database, id: string) => { - return database.execute({ +export const getBook = async (database: Database, id: string): Promise => { + const resp = await database.execute({ sql: "SELECT rowid, * FROM books where rowId = $id;", args: { id } - }) + }); + + return { + id: resp.rows[0]['rowid'] as number, + name: resp.rows[0]['name'] as string, + author: resp.rows[0]['author'] as number, + filepath: resp.rows[0]['file_path'] as string, + } } export const getBookByName = (database: Database, name: string) => { @@ -47,6 +63,17 @@ export const addBookIfNotExists = async (database: Database, book: Book) => { }); } +export const getAuthorById = async (database: Database, id: number): Promise => { + const resp = await database.execute({ + sql: "SELECT rowid, * FROM authors where rowId = $id", + args: {id} + }); + return { + id: resp.rows[0]['rowid'] as number, + name: resp.rows[0]['name'] as string, + } +} + export const getAuthorByName = (database: Database, name: string) => { return database.execute({ sql: "SELECT rowid, * FROM authors WHERE name = $name", @@ -54,6 +81,20 @@ export const getAuthorByName = (database: Database, name: string) => { }) } +export const getAllAuthors = async (database: Database): Promise => { + const resp = await database.execute({ + sql: "SELECT rowid, * FROM authors", + args: {name: name} + }); + + return resp.rows.map((r) => { + return { + id: r['rowid'] as number, + name: r['name'] as string, + } + }) +} + // TODO: Need to ensure author exists when we put the book in the DB. export const addAuthorIfNotExists = async (database: Database, authorName: string): Promise => { const existing = await getAuthorByName(database, authorName); diff --git a/src/books/routes.ts b/src/books/routes.ts index 35e83f8..6c61e68 100644 --- a/src/books/routes.ts +++ b/src/books/routes.ts @@ -1,29 +1,52 @@ -import type { Router } from "@oak/oak"; +import { Status, type Router } from "@oak/oak"; +import { bookService } from "./book-service.ts"; +import { dustService } from "../../main.ts"; export const registerRoutes = (router: Router) => { - router.get("/books/", async (ctx) => { - ctx.response.body = { - books: [], - }; - }); - router.get("/books/:id", (ctx) => { - ctx.response.body = { - book: { - id: ctx.params.id - } - } - }); - router.get("/books/:id/progress", (ctx) => { - ctx.response.body = { - book: { - id: ctx.params.id, - }, - progress: { - page: 0, - } - }; - }); - router.put("/books/:id/progress", (ctx) => { - ctx.response.status = 200; - }); -} \ No newline at end of file + router.get("/books/", async (ctx) => { + const books = await bookService.getBooks(dustService.database); + console.log(books); + ctx.response.body = { + books: books, + }; + }); + router.get("/books/:id", async (ctx) => { + try { + const book = await bookService.getBookById( + dustService.database, + ctx.params.id + ); + console.log(book); + ctx.response.status = Status.OK; + ctx.response.body = { + book: book, + }; + ctx.response.type = "json"; + + return; + } catch (e) { + console.error(e); + } + }); + router.get("/books/:id/stream", async (ctx) => { + const book = await bookService.getBookById( + dustService.database, + ctx.params.id + ); + const file = await Deno.open(book.filepath, { read: true }); + ctx.response.body = file; + }); + router.get("/books/:id/progress", (ctx) => { + ctx.response.body = { + book: { + id: ctx.params.id, + }, + progress: { + page: 0, + }, + }; + }); + router.put("/books/:id/progress", (ctx) => { + ctx.response.status = 200; + }); +};