divan/web/src/pages/ebooks/file/[id].ts
Anas Rashid 0a507d1fdd E-books as a section of every poet, with tabs and covers; a right-to-left PDF reader; page bookmarks; book details
- ای بکس 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>
2026-10-09 15:16:06 +02:00

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 });
};