Refactor Indeehub integration and enhance deployment documentation

- Updated Indeehub references throughout the codebase, changing the name from "IndeedHub" to "Indeehub" for consistency.
- Implemented a virtual app structure for Indeehub, allowing it to open an external URL without requiring a container.
- Enhanced deployment scripts and documentation to clarify SSH access and password management for Indeehub.
- Improved error handling and retry logic in various components to ensure better user experience during onboarding and app interactions.
- Updated CSS for visual enhancements and added new buttons for improved navigation in the AppLauncherOverlay.
This commit is contained in:
Dorian
2026-03-01 17:53:18 +00:00
parent 2c15311ab6
commit 7a05e11834
34 changed files with 877 additions and 163 deletions

View File

@@ -24,44 +24,66 @@ class RPCClient {
async call<T>(options: RPCOptions): Promise<T> {
const { method, params = {}, timeout = 30000 } = options
const maxRetries = 3
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), timeout)
for (let attempt = 0; attempt < maxRetries; attempt++) {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), timeout)
try {
const response = await fetch(this.baseUrl, {
method: 'POST',
credentials: 'include', // Important for session cookies
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ method, params }),
signal: controller.signal,
})
try {
const response = await fetch(this.baseUrl, {
method: 'POST',
credentials: 'include', // Important for session cookies
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ method, params }),
signal: controller.signal,
})
clearTimeout(timeoutId)
clearTimeout(timeoutId)
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
const data: RPCResponse<T> = await response.json()
if (data.error) {
throw new Error(data.error.message || 'RPC Error')
}
return data.result as T
} catch (error) {
clearTimeout(timeoutId)
if (error instanceof Error) {
if (error.name === 'AbortError') {
throw new Error('Request timeout')
if (!response.ok) {
const err = new Error(`HTTP ${response.status}: ${response.statusText}`)
const isRetryable = response.status === 502 || response.status === 503
if (isRetryable && attempt < maxRetries - 1) {
await new Promise((r) => setTimeout(r, 600 * (attempt + 1)))
continue
}
throw err
}
throw error
const data: RPCResponse<T> = await response.json()
if (data.error) {
throw new Error(data.error.message || 'RPC Error')
}
return data.result as T
} catch (error) {
clearTimeout(timeoutId)
if (error instanceof Error) {
if (error.name === 'AbortError') {
const timeoutErr = new Error('Request timeout')
if (attempt < maxRetries - 1) {
await new Promise((r) => setTimeout(r, 600 * (attempt + 1)))
continue
}
throw timeoutErr
}
const msg = error.message
const isRetryable = /502|503|Bad Gateway|fetch|network/i.test(msg)
if (isRetryable && attempt < maxRetries - 1) {
await new Promise((r) => setTimeout(r, 600 * (attempt + 1)))
continue
}
throw error
}
throw new Error('Unknown error occurred')
}
throw new Error('Unknown error occurred')
}
throw new Error('Request failed after retries')
}
// Convenience methods