From a66810bbb12c685ecdabe4db93953f208b19c477 Mon Sep 17 00:00:00 2001 From: Josh Date: Fri, 29 May 2026 08:59:02 -0400 Subject: [PATCH 1/2] fix(collabora): poll built-in CODE status until ready during cold start Signed-off-by: Josh --- src/services/collabora.js | 53 +++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/services/collabora.js b/src/services/collabora.js index 85abb037bd..d1e8d1d47a 100644 --- a/src/services/collabora.js +++ b/src/services/collabora.js @@ -10,6 +10,12 @@ export const LOADING_ERROR = { PROXY_FAILED: 2, } +const PROXY_STARTING_STATES = new Set(['starting', 'stopped', 'restarting']) +const PROXY_POLL_INTERVAL_MS = 1500 +const PROXY_POLL_TIMEOUT_MS = 30000 + +const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) + export const isCollaboraConfigured = () => { const collaboraCapabilities = getCapabilities()?.collabora return isBuiltinCodeServerUsed() || collaboraCapabilities.length !== 0 @@ -26,8 +32,7 @@ export const checkCollaboraConfiguration = async () => { } } -let proxyStatusCheckRetry = 0 -export const checkProxyStatus = async (_resolve, _reject) => { +export const checkProxyStatus = async () => { const wopiUrl = getCapabilities()?.config?.wopi_url if (wopiUrl.indexOf('proxy.php') === -1) { return true @@ -35,34 +40,38 @@ export const checkProxyStatus = async (_resolve, _reject) => { const url = wopiUrl.slice(0, wopiUrl.indexOf('proxy.php') + 'proxy.php'.length) const proxyStatusUrl = url + '?status' + const deadline = Date.now() + PROXY_POLL_TIMEOUT_MS + + while (Date.now() < deadline) { + let result + try { + result = await axios.get(proxyStatusUrl) + } catch (e) { + await sleep(PROXY_POLL_INTERVAL_MS) + continue + } - const checkProxyStatusCallback = async (resolve, reject) => { - const result = await axios.get(proxyStatusUrl) - if (!result || !result?.data?.status) { - reject('Failed to contact status endpoint') + const status = result?.data?.status + if (!status) { + await sleep(PROXY_POLL_INTERVAL_MS) + continue } - if (result.data.status === 'OK') { - return resolve(true) + if (status === 'OK') { + return true } - if (result.data.status === 'error') { - return reject(t('richdocuments', 'Built-in CODE server failed to start')) + + if (status === 'error') { + throw Error(LOADING_ERROR.PROXY_FAILED) } - if (proxyStatusCheckRetry < 3 && (result.data.status === 'starting' || result.data.status === 'stopped' || result.data.status === 'restarting')) { - setTimeout(() => { - proxyStatusCheckRetry++ - checkProxyStatus(resolve, reject) - }) - } else { - reject('Maximum retries reached') + if (PROXY_STARTING_STATES.has(status)) { + await sleep(PROXY_POLL_INTERVAL_MS) + continue } + await sleep(PROXY_POLL_INTERVAL_MS) } - if (_reject && _resolve) { - return checkProxyStatusCallback(_reject, _resolve) - } else { - return new Promise(checkProxyStatusCallback) - } + throw Error(t('richdocuments', 'Starting the built-in CODE server is taking longer than expected')) } From 8db89d2dbf1247b47158a24cc8d045bebd3894c3 Mon Sep 17 00:00:00 2001 From: Josh Date: Fri, 29 May 2026 09:00:39 -0400 Subject: [PATCH 2/2] fix(office): show built-in CODE startup message while waiting for proxy readiness Signed-off-by: Josh --- src/view/Office.vue | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/view/Office.vue b/src/view/Office.vue index 563c1a4a25..fdfb124f5e 100644 --- a/src/view/Office.vue +++ b/src/view/Office.vue @@ -100,7 +100,12 @@ import { } from '../helpers/url.js' import PostMessageService from '../services/postMessage.tsx' import FilesAppIntegration from './FilesAppIntegration.js' -import { LOADING_ERROR, checkCollaboraConfiguration, checkProxyStatus } from '../services/collabora.js' +import { + LOADING_ERROR, + checkCollaboraConfiguration, + checkProxyStatus, + isBuiltinCodeServerUsed, +} from '../services/collabora.js' import { enableScrollLock, disableScrollLock } from '../helpers/mobileFixer.js' import axios from '@nextcloud/axios' import { @@ -277,7 +282,11 @@ export default { }) try { await checkCollaboraConfiguration() + if (isBuiltinCodeServerUsed()) { + this.loadingMsg = t('richdocuments', 'Starting the built-in CODE server …') + } await checkProxyStatus() + this.loadingMsg = null } catch (e) { this.error = e.message this.loading = LOADING_STATE.FAILED @@ -408,16 +417,16 @@ export default { this.documentReady() if (loadState('richdocuments', 'open_local_editor', true) && !this.isEmbedded) { - this.sendPostMessage('Insert_Button', { - id: 'Open_Local_Editor', - imgurl: window.location.protocol + '//' + getNextcloudUrl() + imagePath('richdocuments', 'launch.svg'), - mobile: false, - label: t('richdocuments', 'Open in local editor'), - hint: t('richdocuments', 'Open in local editor'), - insertBefore: 'print', - accessKey: '2', - }) - } + this.sendPostMessage('Insert_Button', { + id: 'Open_Local_Editor', + imgurl: window.location.protocol + '//' + getNextcloudUrl() + imagePath('richdocuments', 'launch.svg'), + mobile: false, + label: t('richdocuments', 'Open in local editor'), + hint: t('richdocuments', 'Open in local editor'), + insertBefore: 'print', + accessKey: '2', + }) + } if (this.isEmbedded && this.hasWidgetEditingEnabled) { this.sendPostMessage('Hide_Sidebar')