diff --git a/.github/workflows/astroglobe-ci.yml b/.github/workflows/astroglobe-ci.yml index c48282a..a7deb12 100644 --- a/.github/workflows/astroglobe-ci.yml +++ b/.github/workflows/astroglobe-ci.yml @@ -247,7 +247,7 @@ jobs: - name: Install OCP for static analysis run: | # Get first OCP version from matrix - OCP_VERSION=$(echo '${{ steps.ocp-versions.outputs.ocp-matrix }}' | jq -r '.[0]."ocp-version"') + OCP_VERSION=$(echo '${{ steps.ocp-versions.outputs.ocp-matrix }}' | jq -r '.include[0]."ocp-version"') composer require --dev "nextcloud/ocp:$OCP_VERSION" --ignore-platform-reqs --with-dependencies - name: Run Psalm diff --git a/third_party/astroglobe/lib/Controller/ApiController.php b/third_party/astroglobe/lib/Controller/ApiController.php index c1e525e..02ceecb 100644 --- a/third_party/astroglobe/lib/Controller/ApiController.php +++ b/third_party/astroglobe/lib/Controller/ApiController.php @@ -12,7 +12,6 @@ use OCA\Astroglobe\Settings\Admin as AdminSettings; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\NoAdminRequired; -use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\RedirectResponse; use OCP\IConfig; @@ -44,7 +43,7 @@ class ApiController extends Controller { LoggerInterface $logger, McpTokenStorage $tokenStorage, IConfig $config, - IdpTokenRefresher $tokenRefresher + IdpTokenRefresher $tokenRefresher, ) { parent::__construct($appName, $request); $this->client = $client; @@ -126,7 +125,7 @@ class ApiController extends Controller { string $algorithm = 'hybrid', int $limit = 10, string $doc_types = '', - string $include_pca = 'true' + string $include_pca = 'true', ): JSONResponse { if (empty($query)) { return new JSONResponse([ @@ -185,7 +184,7 @@ class ApiController extends Controller { $validDocTypes = ['note', 'file', 'deck_card', 'calendar', 'contact', 'news_item']; $docTypesArray = array_filter( explode(',', $doc_types), - fn($t) => in_array(trim($t), $validDocTypes) + fn ($t) => in_array(trim($t), $validDocTypes) ); $docTypesArray = array_map('trim', $docTypesArray); if (empty($docTypesArray)) { @@ -678,7 +677,7 @@ class ApiController extends Controller { string $doc_type, string $doc_id, int $start, - int $end + int $end, ): JSONResponse { $user = $this->userSession->getUser(); if (!$user) { diff --git a/third_party/astroglobe/lib/Controller/OAuthController.php b/third_party/astroglobe/lib/Controller/OAuthController.php index b539126..263b922 100644 --- a/third_party/astroglobe/lib/Controller/OAuthController.php +++ b/third_party/astroglobe/lib/Controller/OAuthController.php @@ -47,7 +47,7 @@ class OAuthController extends Controller { McpTokenStorage $tokenStorage, LoggerInterface $logger, IL10N $l, - IClientService $clientService + IClientService $clientService, ) { parent::__construct($appName, $request); $this->config = $config; @@ -73,11 +73,11 @@ class OAuthController extends Controller { #[NoAdminRequired] #[NoCSRFRequired] public function initiateOAuth() { - $this->logger->info("initiateOAuth called"); + $this->logger->info('initiateOAuth called'); $user = $this->userSession->getUser(); if (!$user) { - $this->logger->error("initiateOAuth: User not authenticated"); + $this->logger->error('initiateOAuth: User not authenticated'); return new TemplateResponse( 'astroglobe', 'settings/error', @@ -85,7 +85,7 @@ class OAuthController extends Controller { ); } - $this->logger->info("initiateOAuth: User authenticated: " . $user->getUID()); + $this->logger->info('initiateOAuth: User authenticated: ' . $user->getUID()); try { // Get MCP server configuration @@ -107,9 +107,9 @@ class OAuthController extends Controller { $codeVerifier = bin2hex(random_bytes(32)); $codeChallenge = $this->base64UrlEncode(hash('sha256', $codeVerifier, true)); - $this->logger->info("Using public client mode with PKCE"); + $this->logger->info('Using public client mode with PKCE'); } else { - $this->logger->info("Using confidential client mode with client secret"); + $this->logger->info('Using confidential client mode with client secret'); } // Generate state for CSRF protection @@ -129,7 +129,7 @@ class OAuthController extends Controller { $codeChallenge ); - $this->logger->info("Initiating OAuth flow for user: " . $user->getUID()); + $this->logger->info('Initiating OAuth flow for user: ' . $user->getUID()); return new RedirectResponse($authUrl); } catch (\Exception $e) { @@ -163,7 +163,7 @@ class OAuthController extends Controller { string $code = '', string $state = '', ?string $error = null, - ?string $error_description = null + ?string $error_description = null, ): RedirectResponse { try { // Check for errors from IdP @@ -292,7 +292,7 @@ class OAuthController extends Controller { private function buildAuthorizationUrl( string $mcpServerUrl, string $state, - ?string $codeChallenge + ?string $codeChallenge, ): string { // First, query MCP server to discover which IdP it's configured to use $this->logger->info('buildAuthorizationUrl: Starting', [ @@ -430,7 +430,7 @@ class OAuthController extends Controller { private function exchangeCodeForToken( string $mcpServerUrl, string $code, - ?string $codeVerifier + ?string $codeVerifier, ): array { // Query MCP server to discover which IdP it's configured to use try { @@ -496,11 +496,11 @@ class OAuthController extends Controller { if (!empty($clientSecret)) { // Confidential client: use client secret for authentication $postData['client_secret'] = $clientSecret; - $this->logger->info("Using client secret for token exchange"); + $this->logger->info('Using client secret for token exchange'); } elseif ($codeVerifier !== null) { // Public client: use PKCE proof for authentication $postData['code_verifier'] = $codeVerifier; - $this->logger->info("Using PKCE code verifier for token exchange"); + $this->logger->info('Using PKCE code verifier for token exchange'); } else { throw new \Exception('Neither client_secret nor code_verifier available for token exchange'); } diff --git a/third_party/astroglobe/lib/Search/SemanticSearchProvider.php b/third_party/astroglobe/lib/Search/SemanticSearchProvider.php index c833905..7734c57 100644 --- a/third_party/astroglobe/lib/Search/SemanticSearchProvider.php +++ b/third_party/astroglobe/lib/Search/SemanticSearchProvider.php @@ -247,8 +247,8 @@ class SemanticSearchProvider implements IProvider { : $this->urlGenerator->linkToRouteAbsolute('files.view.index'), 'deck_card' => isset($result['board_id']) && $id - ? $this->urlGenerator->linkToRoute('deck.page.index') . - "board/{$result['board_id']}/card/{$id}" + ? $this->urlGenerator->linkToRoute('deck.page.index') + . "board/{$result['board_id']}/card/{$id}" : $this->urlGenerator->linkToRoute('deck.page.index'), 'calendar', 'calendar_event' => $this->urlGenerator->linkToRoute('calendar.view.index'), diff --git a/third_party/astroglobe/lib/Service/IdpTokenRefresher.php b/third_party/astroglobe/lib/Service/IdpTokenRefresher.php index 6dda534..aada7c2 100644 --- a/third_party/astroglobe/lib/Service/IdpTokenRefresher.php +++ b/third_party/astroglobe/lib/Service/IdpTokenRefresher.php @@ -25,7 +25,7 @@ class IdpTokenRefresher { public function __construct( IConfig $config, IClientService $clientService, - LoggerInterface $logger + LoggerInterface $logger, ) { $this->config = $config; $this->httpClient = $clientService->newClient(); diff --git a/third_party/astroglobe/lib/Service/McpServerClient.php b/third_party/astroglobe/lib/Service/McpServerClient.php index 5c1de28..83f6f64 100644 --- a/third_party/astroglobe/lib/Service/McpServerClient.php +++ b/third_party/astroglobe/lib/Service/McpServerClient.php @@ -24,7 +24,7 @@ class McpServerClient { public function __construct( IClientService $clientService, IConfig $config, - LoggerInterface $logger + LoggerInterface $logger, ) { $this->httpClient = $clientService->newClient(); $this->config = $config; @@ -85,7 +85,7 @@ class McpServerClient { public function getUserSession(string $userId, string $token): array { try { $response = $this->httpClient->get( - $this->baseUrl . "/api/v1/users/" . urlencode($userId) . "/session", + $this->baseUrl . '/api/v1/users/' . urlencode($userId) . '/session', [ 'headers' => [ 'Authorization' => 'Bearer ' . $token @@ -120,7 +120,7 @@ class McpServerClient { public function revokeUserAccess(string $userId, string $token): array { try { $response = $this->httpClient->post( - $this->baseUrl . "/api/v1/users/" . urlencode($userId) . "/revoke", + $this->baseUrl . '/api/v1/users/' . urlencode($userId) . '/revoke', [ 'headers' => [ 'Authorization' => 'Bearer ' . $token @@ -203,7 +203,7 @@ class McpServerClient { int $limit = 10, bool $includePca = true, ?array $docTypes = null, - ?string $token = null + ?string $token = null, ): array { try { $requestBody = [ @@ -284,7 +284,7 @@ class McpServerClient { int $offset = 0, string $algorithm = 'hybrid', string $fusion = 'rrf', - float $scoreThreshold = 0.0 + float $scoreThreshold = 0.0, ): array { try { $response = $this->httpClient->post( @@ -416,7 +416,7 @@ class McpServerClient { string $event, string $uri, ?array $eventFilter, - string $token + string $token, ): array { try { $requestBody = [ @@ -549,7 +549,7 @@ class McpServerClient { string $docId, int $start, int $end, - string $token + string $token, ): array { try { $response = $this->httpClient->get( diff --git a/third_party/astroglobe/lib/Service/McpTokenStorage.php b/third_party/astroglobe/lib/Service/McpTokenStorage.php index 48d0164..435a1c0 100644 --- a/third_party/astroglobe/lib/Service/McpTokenStorage.php +++ b/third_party/astroglobe/lib/Service/McpTokenStorage.php @@ -22,7 +22,7 @@ class McpTokenStorage { public function __construct( IConfig $config, ICrypto $crypto, - LoggerInterface $logger + LoggerInterface $logger, ) { $this->config = $config; $this->crypto = $crypto; @@ -43,7 +43,7 @@ class McpTokenStorage { string $userId, string $accessToken, string $refreshToken, - int $expiresAt + int $expiresAt, ): void { try { $tokenData = [ @@ -158,7 +158,7 @@ class McpTokenStorage { * * @param string $userId User ID * @param callable|null $refreshCallback Callback to refresh token if expired - * Should accept (refreshToken) and return new token data + * Should accept (refreshToken) and return new token data * @return string|null Access token, or null if not available */ public function getAccessToken(string $userId, ?callable $refreshCallback = null): ?string { diff --git a/third_party/astroglobe/lib/Service/WebhookPresets.php b/third_party/astroglobe/lib/Service/WebhookPresets.php index e3200ff..2db1389 100644 --- a/third_party/astroglobe/lib/Service/WebhookPresets.php +++ b/third_party/astroglobe/lib/Service/WebhookPresets.php @@ -12,24 +12,24 @@ namespace OCA\Astroglobe\Service; */ class WebhookPresets { // File/Notes webhook events - public const FILE_EVENT_CREATED = "OCP\\Files\\Events\\Node\\NodeCreatedEvent"; - public const FILE_EVENT_WRITTEN = "OCP\\Files\\Events\\Node\\NodeWrittenEvent"; + public const FILE_EVENT_CREATED = 'OCP\\Files\\Events\\Node\\NodeCreatedEvent'; + public const FILE_EVENT_WRITTEN = 'OCP\\Files\\Events\\Node\\NodeWrittenEvent'; // Use BeforeNodeDeletedEvent instead of NodeDeletedEvent to get node.id // See: https://github.com/nextcloud/server/issues/56371 - public const FILE_EVENT_DELETED = "OCP\\Files\\Events\\Node\\BeforeNodeDeletedEvent"; + public const FILE_EVENT_DELETED = 'OCP\\Files\\Events\\Node\\BeforeNodeDeletedEvent'; // Calendar webhook events - public const CALENDAR_EVENT_CREATED = "OCP\\Calendar\\Events\\CalendarObjectCreatedEvent"; - public const CALENDAR_EVENT_UPDATED = "OCP\\Calendar\\Events\\CalendarObjectUpdatedEvent"; - public const CALENDAR_EVENT_DELETED = "OCP\\Calendar\\Events\\CalendarObjectDeletedEvent"; + public const CALENDAR_EVENT_CREATED = 'OCP\\Calendar\\Events\\CalendarObjectCreatedEvent'; + public const CALENDAR_EVENT_UPDATED = 'OCP\\Calendar\\Events\\CalendarObjectUpdatedEvent'; + public const CALENDAR_EVENT_DELETED = 'OCP\\Calendar\\Events\\CalendarObjectDeletedEvent'; // Tables webhook events (Nextcloud 30+) - public const TABLES_EVENT_ROW_ADDED = "OCA\\Tables\\Event\\RowAddedEvent"; - public const TABLES_EVENT_ROW_UPDATED = "OCA\\Tables\\Event\\RowUpdatedEvent"; - public const TABLES_EVENT_ROW_DELETED = "OCA\\Tables\\Event\\RowDeletedEvent"; + public const TABLES_EVENT_ROW_ADDED = 'OCA\\Tables\\Event\\RowAddedEvent'; + public const TABLES_EVENT_ROW_UPDATED = 'OCA\\Tables\\Event\\RowUpdatedEvent'; + public const TABLES_EVENT_ROW_DELETED = 'OCA\\Tables\\Event\\RowDeletedEvent'; // Forms webhook events (Nextcloud 30+) - public const FORMS_EVENT_FORM_SUBMITTED = "OCA\\Forms\\Events\\FormSubmittedEvent"; + public const FORMS_EVENT_FORM_SUBMITTED = 'OCA\\Forms\\Events\\FormSubmittedEvent'; // NOTE: Deck and Contacts do NOT support webhooks // Their event classes do not implement IWebhookCompatibleEvent interface. @@ -163,7 +163,7 @@ class WebhookPresets { } return array_map( - fn($eventConfig) => $eventConfig['event'], + fn ($eventConfig) => $eventConfig['event'], $preset['events'] ); } diff --git a/third_party/astroglobe/lib/Settings/Admin.php b/third_party/astroglobe/lib/Settings/Admin.php index dffde05..3654e65 100644 --- a/third_party/astroglobe/lib/Settings/Admin.php +++ b/third_party/astroglobe/lib/Settings/Admin.php @@ -36,7 +36,7 @@ class Admin implements ISettings { public function __construct( McpServerClient $client, IConfig $config, - IInitialState $initialState + IInitialState $initialState, ) { $this->client = $client; $this->config = $config; diff --git a/third_party/astroglobe/lib/Settings/Personal.php b/third_party/astroglobe/lib/Settings/Personal.php index 39ab36c..abb1f04 100644 --- a/third_party/astroglobe/lib/Settings/Personal.php +++ b/third_party/astroglobe/lib/Settings/Personal.php @@ -33,7 +33,7 @@ class Personal implements ISettings { IUserSession $userSession, IInitialState $initialState, McpTokenStorage $tokenStorage, - IURLGenerator $urlGenerator + IURLGenerator $urlGenerator, ) { $this->client = $client; $this->userSession = $userSession; diff --git a/third_party/astroglobe/src/App.vue b/third_party/astroglobe/src/App.vue index e4c6146..92de959 100644 --- a/third_party/astroglobe/src/App.vue +++ b/third_party/astroglobe/src/App.vue @@ -118,7 +118,7 @@ min="0" max="100" step="5" - class="mcp-score-slider" /> + class="mcp-score-slider"> @@ -248,29 +248,43 @@
-
{{ t('astroglobe', 'Sync Status') }}
+
+ {{ t('astroglobe', 'Sync Status') }} +
{{ vectorStatus.status }}
-
{{ t('astroglobe', 'Indexed Documents') }}
-
{{ vectorStatus.indexed_documents || 0 }}
+
+ {{ t('astroglobe', 'Indexed Documents') }} +
+
+ {{ vectorStatus.indexed_documents || 0 }} +
-
{{ t('astroglobe', 'Pending Documents') }}
-
{{ vectorStatus.pending_documents || 0 }}
+
+ {{ t('astroglobe', 'Pending Documents') }} +
+
+ {{ vectorStatus.pending_documents || 0 }} +
-
{{ t('astroglobe', 'Last Sync') }}
-
{{ vectorStatus.last_sync_time }}
+
+ {{ t('astroglobe', 'Last Sync') }} +
+
+ {{ vectorStatus.last_sync_time }} +
- + @@ -308,9 +322,15 @@
-
{{ viewerContext.before }}
-
{{ viewerContext.chunk }}
-
{{ viewerContext.after }}
+
+ {{ viewerContext.before }} +
+
+ {{ viewerContext.chunk }} +
+
+ {{ viewerContext.after }} +
@@ -337,8 +357,6 @@ import Cog from 'vue-material-design-icons/Cog.vue' import ChevronDown from 'vue-material-design-icons/ChevronDown.vue' import ChevronUp from 'vue-material-design-icons/ChevronUp.vue' import Refresh from 'vue-material-design-icons/Refresh.vue' -import TextBoxOutline from 'vue-material-design-icons/TextBoxOutline.vue' -import TextBoxRemoveOutline from 'vue-material-design-icons/TextBoxRemoveOutline.vue' import OpenInNew from 'vue-material-design-icons/OpenInNew.vue' import Eye from 'vue-material-design-icons/Eye.vue' import Close from 'vue-material-design-icons/Close.vue' @@ -354,16 +372,13 @@ import * as pdfjsLib from 'pdfjs-dist' try { pdfjsLib.GlobalWorkerOptions.workerSrc = new URL( 'pdfjs-dist/build/pdf.worker.mjs', - import.meta.url + import.meta.url, ).toString() } catch (e) { console.warn('Failed to set PDF.js worker, will use fallback', e) // PDF.js will use fake worker automatically } -// App name for translations -const APP_NAME = 'astroglobe' - export default { name: 'App', components: { @@ -385,8 +400,6 @@ export default { ChevronDown, ChevronUp, Refresh, - TextBoxOutline, - TextBoxRemoveOutline, OpenInNew, Eye, Close, @@ -754,7 +767,7 @@ export default { closeViewer() { this.showViewer = false - } + }, }, } @@ -1177,7 +1190,7 @@ a.mcp-result-title { .mcp-checkbox-grid { grid-template-columns: 1fr; } - + .mcp-modal { width: 100%; height: 100%; diff --git a/third_party/astroglobe/src/adminSettings.js b/third_party/astroglobe/src/adminSettings.js index 639d94b..3cb3d8b 100644 --- a/third_party/astroglobe/src/adminSettings.js +++ b/third_party/astroglobe/src/adminSettings.js @@ -59,7 +59,7 @@ function initSearchSettingsForm() { const response = await axios.post( generateUrl('/apps/astroglobe/api/admin/search-settings'), data, - { headers: { 'Content-Type': 'application/json' } } + { headers: { 'Content-Type': 'application/json' } }, ) if (response.data.success) { @@ -91,7 +91,7 @@ async function initWebhookManagement() { try { // Load webhook presets from API const response = await axios.get( - generateUrl('/apps/astroglobe/api/admin/webhooks/presets') + generateUrl('/apps/astroglobe/api/admin/webhooks/presets'), ) if (!response.data.success) { @@ -115,7 +115,7 @@ async function initWebhookManagement() { * Render webhook preset cards. * * @param {HTMLElement} container Container element - * @param {Object} presets Preset configurations + * @param {object} presets Preset configurations */ function renderWebhookPresets(container, presets) { const presetIds = Object.keys(presets) @@ -147,7 +147,7 @@ function renderWebhookPresets(container, presets) { * Create a webhook preset card. * * @param {string} presetId Preset ID - * @param {Object} preset Preset configuration + * @param {object} preset Preset configuration * @return {HTMLElement} Card element */ function createPresetCard(presetId, preset) { @@ -212,7 +212,6 @@ async function togglePreset(presetId, currentlyEnabled) { } // Reload presets to update UI - const container = document.getElementById('webhook-presets-container') await initWebhookManagement() // Show success notification @@ -227,7 +226,7 @@ async function togglePreset(presetId, currentlyEnabled) { // Show error notification OC.Notification.showTemporary( error.message || 'Failed to toggle webhook preset', - { type: 'error' } + { type: 'error' }, ) } } diff --git a/third_party/astroglobe/src/components/PDFViewer.vue b/third_party/astroglobe/src/components/PDFViewer.vue index d33c507..b45e717 100644 --- a/third_party/astroglobe/src/components/PDFViewer.vue +++ b/third_party/astroglobe/src/components/PDFViewer.vue @@ -8,8 +8,8 @@

{{ error }}

-
- +
+
t('Unknown')); ?> @@ -150,8 +150,8 @@ style('astroglobe', 'astroglobe-settings'); + $statusClass = $status === 'idle' ? 'success' : ($status === 'syncing' ? 'info' : 'neutral'); + ?> @@ -177,8 +177,8 @@ style('astroglobe', 'astroglobe-settings'); t('Errors (24h)')); ?> 0): ?> + $errors = $_['vectorSyncStatus']['errors_24h'] ?? 0; + if ($errors > 0): ?> @@ -213,13 +213,19 @@ style('astroglobe', 'astroglobe-settings');
@@ -231,10 +237,14 @@ style('astroglobe', 'astroglobe-settings');