diff --git a/services/library-desk/static/wikijs-integration.js b/services/library-desk/static/wikijs-integration.js index 36b461e..0443d75 100644 --- a/services/library-desk/static/wikijs-integration.js +++ b/services/library-desk/static/wikijs-integration.js @@ -28,6 +28,10 @@ } } + // Store page ID globally once fetched + let CACHED_PAGE_ID = null; + let CACHED_PAGE_PATH = null; + // Initialize when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); @@ -39,11 +43,11 @@ log('Initializing Library Desk integration...'); log('Library Desk URL:', CONFIG.libraryDeskUrl); - // Wait for Wiki.js to fully render + // Wait for page to be ready, then add buttons setTimeout(addButtons, 1500); } - function addButtons() { + async function addButtons() { const pagePath = getPagePath(); if (!pagePath) { log('Skipping buttons - not on a content page'); @@ -58,6 +62,11 @@ return; } + // Fetch and cache page ID during initialization + CACHED_PAGE_PATH = pagePath; + CACHED_PAGE_ID = await getPageIdFromPath(pagePath); + log('Cached page ID for this session:', CACHED_PAGE_ID); + if (CONFIG.buttonPosition === 'floating') { addFloatingButtons(pagePath); } else { @@ -210,33 +219,15 @@ } try { - log('Fetching page list...'); - - const listUrl = CONFIG.libraryDeskUrl + '/wiki/pages?user=' + CONFIG.user + '&limit=200'; - const listResponse = await fetch(listUrl, { - headers: { 'Authorization': 'Bearer ' + CONFIG.apiKey } - }); - - if (!listResponse.ok) { - throw new Error('Failed to fetch page list: ' + listResponse.status); + // Use cached page ID from initialization + if (!CACHED_PAGE_ID) { + throw new Error('Page ID not available - was not fetched during initialization'); } - const pageList = await listResponse.json(); - log('Fetched', pageList.pages.length, 'pages'); + log('Re-indexing page', CACHED_PAGE_ID, '...'); - // Find matching page - const page = findMatchingPage(pageList.pages, pagePath); - - if (!page) { - log('Available pages:', pageList.pages.map(p => p.path)); - throw new Error('Page not found in Library Desk. Check console for available pages.'); - } - - log('Matched page:', page.id, page.path, page.title); - log('Re-indexing...'); - - // Re-index - const response = await fetch(CONFIG.libraryDeskUrl + '/ingest/batch', { + // Re-index directly + const response = await fetch(CONFIG.libraryDeskUrl + '/ingest/page', { method: 'POST', headers: { 'Authorization': 'Bearer ' + CONFIG.apiKey, @@ -244,15 +235,14 @@ }, body: JSON.stringify({ user: CONFIG.user, - page_ids: [page.id] + page_id: CACHED_PAGE_ID, + force_refresh: true }) }); const result = await response.json(); - if (response.ok && result.successful > 0) { - const pageResult = result.results[0]; - + if (response.ok && result.success) { if (isFloating) { button.innerHTML = '✓ Done!'; button.style.background = '#4caf50'; @@ -261,11 +251,11 @@ icon.style.color = '#4caf50'; } - const message = 'Re-indexed: ' + page.title + '\n\n' + - '✓ Vectors: ' + pageResult.vector_chunks_created + ' chunks\n' + - '✓ Entities: ' + pageResult.graph_entities_extracted + '\n' + - '✓ Relationships: ' + pageResult.graph_relationships_created + '\n' + - '⏱ Time: ' + Math.round(pageResult.processing_time_ms) + 'ms'; + const message = 'Re-indexed: ' + result.page_title + '\n\n' + + '✓ Vectors: ' + result.vector_chunks_created + ' chunks\n' + + '✓ Entities: ' + result.graph_entities_extracted + '\n' + + '✓ Relationships: ' + result.graph_relationships_created + '\n' + + '⏱ Time: ' + Math.round(result.processing_time_ms) + 'ms'; log('Success:', message); @@ -333,28 +323,15 @@ } try { - log('Fetching page list...'); - - // Step 1: Get page ID - const listUrl = CONFIG.libraryDeskUrl + '/wiki/pages?user=' + CONFIG.user + '&limit=200'; - const listResponse = await fetch(listUrl, { - headers: { 'Authorization': 'Bearer ' + CONFIG.apiKey } - }); - - if (!listResponse.ok) { - throw new Error('Failed to fetch page list'); + // Get page ID from Wiki.js GraphQL API + const pageId = await getPageIdFromPath(pagePath); + if (!pageId) { + throw new Error('Could not get page ID from Wiki.js'); } - const pageList = await listResponse.json(); - const page = findMatchingPage(pageList.pages, pagePath); + log('Linking entities for page', pageId, '...'); - if (!page) { - throw new Error('Page not found in Library Desk'); - } - - log('Found page:', page.id, page.title); - - // Step 2: Call entity linking endpoint + // Call entity linking endpoint const response = await fetch(CONFIG.libraryDeskUrl + '/entity-linking/link-page', { method: 'POST', headers: { @@ -363,7 +340,7 @@ }, body: JSON.stringify({ user: CONFIG.user, - page_id: page.id, + page_id: pageId, create_relationships: true, re_index_if_changed: true }) @@ -447,29 +424,94 @@ // SHARED UTILITIES // ============================================================================ - function findMatchingPage(pages, pagePath) { - const possiblePaths = [ - pagePath, - 'users/' + CONFIG.user + '/' + pagePath, - pagePath.replace(/^\//, ''), - CONFIG.user + '/' + pagePath - ]; + async function getPageIdFromPath(pagePath) { + /** + * Query Wiki.js GraphQL API to get page ID from path. + * Uses singleByPath query which requires path and locale. + */ + try { + // Get locale from URL or default to 'en' + const urlLocaleMatch = window.location.pathname.match(/^\/([a-z]{2})\//); + const locale = urlLocaleMatch ? urlLocaleMatch[1] : 'en'; - log('Looking for page matching:', possiblePaths); + log('Querying Wiki.js GraphQL for page:', pagePath, 'locale:', locale); - for (var i = 0; i < pages.length; i++) { - var page = pages[i]; - for (var j = 0; j < possiblePaths.length; j++) { - var testPath = possiblePaths[j]; - if (page.path === testPath || - page.path.endsWith('/' + testPath) || - ('users/' + CONFIG.user + '/' + testPath) === page.path) { - return page; + const query = ` + query ($path: String!, $locale: String!) { + pages { + singleByPath(path: $path, locale: $locale) { + id + } + } } + `; + + const response = await fetch('/graphql', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + credentials: 'same-origin', + body: JSON.stringify({ + query: query, + variables: { path: pagePath, locale: locale } + }) + }); + + const result = await response.json(); + log('GraphQL response:', result); + + if (result.data && result.data.pages && result.data.pages.singleByPath) { + const pageId = result.data.pages.singleByPath.id; + log('Found page ID from GraphQL:', pageId); + return parseInt(pageId, 10); + } + + if (result.errors) { + log('GraphQL errors:', result.errors.map(e => e.message).join(', ')); + } + + log('GraphQL query returned no page'); + return null; + } catch (error) { + log('GraphQL query failed:', error.message); + return null; + } + } + + function getPageId() { + /** + * Synchronous wrapper - returns null and triggers async fetch. + * Actual buttons will need to call getPageIdFromPath() directly. + */ + log('getPageId() called - page ID must be fetched asynchronously via getPageIdFromPath()'); + return null; + } + + function waitForPageElement(callback, maxAttempts = 10, interval = 500) { + /** + * Wait for Vue store to be available, then call callback. + */ + let attempts = 0; + + function check() { + attempts++; + + // Check if Vue store or page data is available + const hasVueStore = window.$store && (window.$store.state || window.$store.get); + const hasPageData = window.$page || window.pageId; + + if (hasVueStore || hasPageData) { + log('Vue/page data available after', attempts, 'attempts'); + callback(); + } else if (attempts < maxAttempts) { + log('Waiting for Vue/page data... attempt', attempts); + setTimeout(check, interval); + } else { + log('Vue/page data never appeared after', maxAttempts, 'attempts - adding buttons anyway'); + callback(); // Add buttons anyway, they just won't work } } - return null; + check(); } function findToolbar() {