- ای بکس card beside the poet's books and genres on every poet page; /<poet>/ebooks with tabs پی ڈی ایف (PDF) / ای پب (ePUB) / یونیکوڈ متن (Unicode) (+ آرکائیو when present) and a grid of covers. - Covers: made in the moderator's browser from a PDF's first page (pdf.js) or an EPUB's cover (epub.js); archive.org's thumbnail; a styled card for text; or the moderator's own image. سرورق بنائیں on existing books. - PDF reader (pdf.js) reading right to left: next page to the left (button, ← key, left half), facing pages with the cover alone then 2|3; left to right for LTR books; full screen (F); opens in a new tab; byte-range requests. - Page bookmarks in the reader's library (kind 'page'), listed in نشانات and opening at that page. - Details: writer as printed, co-authors (linked to poets on the site, who list the book too; new names kept as text), language, reading direction (RTL/LTR), extra tags (on tag pages), source, copyright, note; in upload and review. - Word lookup and selection menu only on reading text, not menus, buttons or tabs. - Schema: the library kind check lists 'page' where it is defined (it was re-added without it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
18 lines
967 B
TypeScript
18 lines
967 B
TypeScript
// An e-book's file, passed through from the private API (with the reader's session, so moderators can open
|
|
// files still under review)
|
|
import type { APIRoute } from 'astro';
|
|
import { COOKIE } from '../../../lib/auth';
|
|
|
|
const API = process.env.API_URL ?? 'http://127.0.0.1:4100';
|
|
|
|
export const GET: APIRoute = async ({ params, cookies, request }) => {
|
|
const token = cookies.get(COOKIE)?.value;
|
|
const range = request.headers.get('range'); // the PDF reader asks for byte ranges
|
|
const res = await fetch(`${API}/api/ebook/${Number(params.id) || 0}/file`, {
|
|
headers: { ...(token && { authorization: `Bearer ${token}` }), ...(range && { range }) },
|
|
});
|
|
const headers = new Headers();
|
|
for (const k of ['content-type', 'content-length', 'content-disposition', 'cache-control', 'accept-ranges', 'content-range']) { const v = res.headers.get(k); if (v) headers.set(k, v); }
|
|
return new Response(res.ok ? res.body : null, { status: res.status, headers });
|
|
};
|