- Add lib/hardcover.ts with GraphQL client for Hardcover API - Add routes/api/books/[name].ts for creating books via Hardcover ID - Add routes/api/books/enhance/[name].ts for enhancing books - Add routes/api/hardcover/query.ts for searching books - Add routes/books/[name].tsx and index.tsx for book pages
76 lines
2.4 KiB
TypeScript
76 lines
2.4 KiB
TypeScript
import { Handlers } from "$fresh/server.ts";
|
|
import { json } from "@lib/helpers.ts";
|
|
import { getBookDetails } from "@lib/hardcover.ts";
|
|
import { fileExtension } from "https://deno.land/x/file_extension@v2.1.0/mod.ts";
|
|
import { formatDate, safeFileName, toUrlSafeString } from "@lib/string.ts";
|
|
import { AccessDeniedError } from "@lib/errors.ts";
|
|
import { createResource, fetchResource } from "@lib/marka/index.ts";
|
|
import { ReviewResource } from "@lib/marka/schema.ts";
|
|
|
|
export const handler: Handlers = {
|
|
async GET(_, ctx) {
|
|
const book = await fetchResource(`books/${ctx.params.name}`);
|
|
return json(book?.content);
|
|
},
|
|
async POST(_, ctx) {
|
|
const session = ctx.state.session;
|
|
if (!session) throw new AccessDeniedError();
|
|
|
|
const hardcoverId = ctx.params.name;
|
|
if (!hardcoverId) throw new AccessDeniedError();
|
|
|
|
const bookDetails = await getBookDetails(hardcoverId);
|
|
|
|
if (!bookDetails) {
|
|
throw new Error("Book not found on Hardcover");
|
|
}
|
|
|
|
const title = bookDetails.title || hardcoverId;
|
|
const authorName = bookDetails.author_names?.[0] || "";
|
|
const isbn = bookDetails.isbn13 || bookDetails.isbn || "";
|
|
const releaseDate = bookDetails.release_year
|
|
? `${bookDetails.release_year}-01-01`
|
|
: undefined;
|
|
|
|
let finalPath = "";
|
|
if (bookDetails.image) {
|
|
try {
|
|
const response = await fetch(bookDetails.image);
|
|
if (response.ok) {
|
|
const buffer = await response.arrayBuffer();
|
|
const extension = fileExtension(bookDetails.image);
|
|
finalPath = `books/images/${safeFileName(title)}_cover.${extension}`;
|
|
await createResource(finalPath, buffer);
|
|
}
|
|
} catch {
|
|
console.log("Failed to download book cover");
|
|
}
|
|
}
|
|
|
|
const book: ReviewResource["content"] = {
|
|
_type: "Review",
|
|
headline: title,
|
|
subtitle: bookDetails.subtitle,
|
|
bookBody: bookDetails.description || "",
|
|
link: `https://hardcover.app/books/${bookDetails.slug}`,
|
|
image: finalPath ? `resources/${finalPath}` : undefined,
|
|
datePublished: formatDate(releaseDate),
|
|
author: authorName ? {
|
|
_type: "Person",
|
|
name: authorName,
|
|
} : undefined,
|
|
itemReviewed: {
|
|
name: title,
|
|
},
|
|
};
|
|
|
|
console.log("Creating book resource:", JSON.stringify(book, null, 2));
|
|
|
|
const fileName = toUrlSafeString(title);
|
|
|
|
await createResource(`books/${fileName}.md`, book);
|
|
|
|
return json({ name: fileName });
|
|
},
|
|
};
|