refactor(astrolabe): replace client-side PDF.js with server-side PyMuPDF rendering

Replace the client-side PDF.js viewer with server-side rendering using PyMuPDF.
This avoids CSP worker restrictions and ES private field access issues that
affected Chromium browsers.

Changes:
- Add /api/v1/pdf-preview endpoint to MCP server (management.py)
- Add pdf-preview route and controller action in Astrolabe PHP backend
- Refactor PDFViewer.vue to display server-rendered PNG images
- Remove pdfjs-dist dependency and client-side PDF loading code
- Use @nextcloud/axios for CSRF token handling in PDFViewer

The server downloads the PDF via WebDAV, renders the requested page with
PyMuPDF at the specified scale, and returns a base64-encoded PNG image.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-01-26 20:04:57 +01:00
co-authored by Claude Opus 4.5
parent bc62f2a066
commit d5544a7731
9 changed files with 325 additions and 162 deletions
+57
View File
@@ -788,4 +788,61 @@ class ApiController extends Controller {
return new JSONResponse($result);
}
/**
* Get PDF page preview (server-side rendered).
*
* AJAX endpoint for PDF viewer in semantic search UI.
* Uses server-side PyMuPDF rendering to avoid CSP/worker issues.
*
* @param string $file_path WebDAV path to PDF file
* @param int $page Page number (1-indexed, default: 1)
* @param float $scale Zoom factor (default: 2.0)
* @return JSONResponse
*/
#[NoAdminRequired]
public function pdfPreview(
string $file_path,
int $page = 1,
float $scale = 2.0,
): JSONResponse {
$user = $this->userSession->getUser();
if (!$user) {
return new JSONResponse(['success' => false, 'error' => 'User not authenticated'], Http::STATUS_UNAUTHORIZED);
}
$userId = $user->getUID();
// Create refresh callback
$refreshCallback = function (string $refreshToken) {
$newTokenData = $this->tokenRefresher->refreshAccessToken($refreshToken);
if (!$newTokenData) {
return null;
}
return [
'access_token' => $newTokenData['access_token'],
'refresh_token' => $newTokenData['refresh_token'] ?? $refreshToken,
'expires_in' => $newTokenData['expires_in'] ?? 3600,
];
};
// Get user's OAuth token for MCP server with automatic refresh
$accessToken = $this->tokenStorage->getAccessToken($userId, $refreshCallback);
if (!$accessToken) {
return new JSONResponse([
'success' => false,
'error' => 'MCP server authorization required.'
], Http::STATUS_UNAUTHORIZED);
}
$result = $this->client->getPdfPreview($file_path, $page, $scale, $accessToken);
if (isset($result['error'])) {
return new JSONResponse(['success' => false, 'error' => $result['error']], Http::STATUS_INTERNAL_SERVER_ERROR);
}
return new JSONResponse($result);
}
}