api = new BookApi(); } public function cover(string $id) { $book = $this->api->book($id); if (! $book || ! $book['coverEndpoint']) { $this->response->setStatusCode(404); return; } // serve from local disk cache once fetched $dir = WRITEPATH . 'cache/covers'; $file = $dir . '/' . preg_replace('/[^a-zA-Z0-9_-]/', '_', $id) . '.jpg'; if (! is_file($file)) { $tmp = $file . '.tmp' . getmypid(); $ok = $this->api->fetchToFile($book['coverEndpoint'], $tmp); if ($ok) { if (! is_dir($dir)) { mkdir($dir, 0775, true); } rename($tmp, $file); } else { @unlink($tmp); } } if (is_file($file)) { header('Content-Type: image/jpeg'); header('Content-Length: ' . (string) filesize($file)); header('Cache-Control: public, max-age=604800'); readfile($file); exit; } $this->response->setHeader('Cache-Control', 'public, max-age=86400'); $this->api->stream($book['coverEndpoint']); exit; } public function pdf(string $id) { // the PDF stream requires an access grant (captcha-verified session) if (! \App\Libraries\PdfAccess::has($id)) { return $this->response->setStatusCode(403)->setJSON([ 'error' => 'Truy cập bị từ chối. Vui lòng xác nhận captcha tại trang sách để xem nội dung.', ]); } $book = $this->api->book($id); if (! $book || ! $book['pdfEndpoint']) { $this->response->setStatusCode(404); return; } // inline disposition: viewer only, not a download header('Content-Disposition: inline'); header('X-Robots-Tag: noindex'); $this->api->stream($book['pdfEndpoint']); exit; } }