fix: disable HTTP keep-alive and update nginx proxy config
- Set http1_keep_alive(false) on hyper server to prevent connection reuse issues with nginx reverse proxy - Clean up nginx proxy config: remove upstream block, use direct proxy_pass to 127.0.0.1:5678 - Update AppLauncherOverlay and appLauncher store with UI fixes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -77,14 +77,39 @@
|
||||
</Transition>
|
||||
<iframe
|
||||
ref="iframeRef"
|
||||
v-if="store.url"
|
||||
v-if="store.url && !iframeBlocked"
|
||||
:key="iframeRefreshKey"
|
||||
:src="store.url"
|
||||
class="absolute inset-0 w-full h-full border-0 iframe-scrollbar-hide"
|
||||
title="App content"
|
||||
@load="onIframeLoad"
|
||||
@error="onIframeError"
|
||||
/>
|
||||
|
||||
<!-- Iframe blocked fallback -->
|
||||
<Transition name="content-fade">
|
||||
<div v-if="iframeBlocked && !iframeLoading" class="absolute inset-0 z-10 flex flex-col items-center justify-center">
|
||||
<div class="text-center px-8">
|
||||
<div class="w-16 h-16 mx-auto mb-4 rounded-2xl bg-white/5 border border-white/10 flex items-center justify-center">
|
||||
<svg class="w-8 h-8 text-white/40" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M3 8V6a2 2 0 012-2h14a2 2 0 012 2v12a2 2 0 01-2 2H5a2 2 0 01-2-2v-2m0-8h18M3 8v8m18-8v8" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold text-white mb-2">Can't display in frame</h3>
|
||||
<p class="text-white/50 text-sm mb-6">This app doesn't support embedded viewing.<br>Please open it in a new tab instead.</p>
|
||||
<button
|
||||
@click="openInNewTabAndClose"
|
||||
class="glass-button px-6 py-3 rounded-lg text-sm font-semibold inline-flex items-center gap-2"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
||||
</svg>
|
||||
Open in new tab
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<!-- Payment Confirmation Dialog -->
|
||||
<Transition name="content-fade">
|
||||
<div v-if="pendingPayment" class="absolute inset-0 z-20 flex items-center justify-center bg-black/70 backdrop-blur-sm">
|
||||
@@ -158,6 +183,16 @@ const iframeRef = ref<HTMLIFrameElement | null>(null)
|
||||
const iframeRefreshKey = ref(0)
|
||||
const isRefreshing = ref(false)
|
||||
const iframeLoading = ref(true)
|
||||
const iframeBlocked = ref(false)
|
||||
|
||||
// Timers for iframe load detection
|
||||
let loadTimeoutId: ReturnType<typeof setTimeout> | null = null
|
||||
let contentCheckId: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
function clearTimers() {
|
||||
if (loadTimeoutId) { clearTimeout(loadTimeoutId); loadTimeoutId = null }
|
||||
if (contentCheckId) { clearTimeout(contentCheckId); contentCheckId = null }
|
||||
}
|
||||
|
||||
// Wallet connect — payment request state
|
||||
const pendingPayment = ref<PaymentRequest | null>(null)
|
||||
@@ -168,7 +203,15 @@ const paymentOrigin = ref('')
|
||||
function refreshIframe() {
|
||||
isRefreshing.value = true
|
||||
iframeLoading.value = true
|
||||
iframeBlocked.value = false
|
||||
clearTimers()
|
||||
iframeRefreshKey.value++
|
||||
loadTimeoutId = setTimeout(() => {
|
||||
if (iframeLoading.value) {
|
||||
iframeLoading.value = false
|
||||
iframeBlocked.value = true
|
||||
}
|
||||
}, 15000)
|
||||
}
|
||||
|
||||
function openInNewTab() {
|
||||
@@ -177,11 +220,44 @@ function openInNewTab() {
|
||||
}
|
||||
}
|
||||
|
||||
function openInNewTabAndClose() {
|
||||
openInNewTab()
|
||||
store.close()
|
||||
}
|
||||
|
||||
function onIframeLoad() {
|
||||
injectScrollbarHideIfSameOrigin()
|
||||
isRefreshing.value = false
|
||||
iframeLoading.value = false
|
||||
sendIdentityIfSupported()
|
||||
|
||||
// Clear the load timeout
|
||||
if (loadTimeoutId) { clearTimeout(loadTimeoutId); loadTimeoutId = null }
|
||||
|
||||
// Check iframe content after a brief delay to let the app render
|
||||
contentCheckId = setTimeout(checkIframeContent, 2000)
|
||||
}
|
||||
|
||||
function onIframeError() {
|
||||
clearTimers()
|
||||
iframeLoading.value = false
|
||||
iframeBlocked.value = true
|
||||
}
|
||||
|
||||
/** Check if the iframe loaded meaningful content (same-origin only) */
|
||||
function checkIframeContent() {
|
||||
try {
|
||||
const iframe = iframeRef.value
|
||||
if (!iframe) return
|
||||
const doc = iframe.contentDocument
|
||||
if (!doc) return // Cross-origin — can't check, assume OK
|
||||
const body = doc.body
|
||||
if (!body || (body.children.length === 0 && body.innerText.trim() === '')) {
|
||||
iframeBlocked.value = true
|
||||
}
|
||||
} catch {
|
||||
// Cross-origin: can't access, assume working
|
||||
}
|
||||
}
|
||||
|
||||
/** Apps that support the Archipelago identity protocol (postMessage) */
|
||||
@@ -379,10 +455,21 @@ watch(
|
||||
(open) => {
|
||||
if (open) {
|
||||
iframeLoading.value = true
|
||||
iframeBlocked.value = false
|
||||
clearTimers()
|
||||
// Set max load timeout — if iframe never fires load, show fallback
|
||||
loadTimeoutId = setTimeout(() => {
|
||||
if (iframeLoading.value) {
|
||||
iframeLoading.value = false
|
||||
iframeBlocked.value = true
|
||||
}
|
||||
}, 15000)
|
||||
closeBtnRef.value?.focus()
|
||||
} else {
|
||||
isRefreshing.value = false
|
||||
iframeLoading.value = true
|
||||
iframeBlocked.value = false
|
||||
clearTimers()
|
||||
// Clear any pending payment when closing
|
||||
if (pendingPayment.value) {
|
||||
rejectPayment()
|
||||
@@ -397,6 +484,7 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearTimers()
|
||||
window.removeEventListener('keydown', onKeyDown, true)
|
||||
window.removeEventListener('message', onMessage)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user