fix: indeedhub staging API, nginx caching, nostr identity and UI improvements
Switch IndeedHub to staging API, add _next asset caching in nginx, simplify NostrIdentityPicker component, and update Apps/Web5/Marketplace views. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
ee15fbc457
commit
b6f401e7f6
@@ -179,14 +179,14 @@
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4"
|
||||
@click="closeUninstallModal()"
|
||||
>
|
||||
<div class="absolute inset-0 bg-black/10 backdrop-blur-md"></div>
|
||||
<div class="absolute inset-0 bg-black/60 backdrop-blur-md"></div>
|
||||
<div
|
||||
ref="uninstallModalRef"
|
||||
@click.stop
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="uninstall-dialog-title"
|
||||
class="glass-card p-6 max-w-md w-full relative z-10"
|
||||
class="glass-card p-6 max-w-2xl w-full relative z-10"
|
||||
>
|
||||
<div class="flex items-start gap-4 mb-4">
|
||||
<div class="p-3 bg-red-500/20 rounded-lg">
|
||||
@@ -211,9 +211,20 @@
|
||||
</button>
|
||||
<button
|
||||
@click="confirmUninstall"
|
||||
class="px-4 py-2 bg-red-600/80 hover:bg-red-600 rounded-lg text-white text-sm font-medium transition-colors"
|
||||
:disabled="uninstalling"
|
||||
class="px-4 py-2 bg-red-600/80 hover:bg-red-600 rounded-lg text-white text-sm font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
|
||||
>
|
||||
{{ t('common.uninstall') }}
|
||||
<svg
|
||||
v-if="uninstalling"
|
||||
class="animate-spin h-4 w-4"
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
<span>{{ uninstalling ? t('common.uninstalling') : t('common.uninstall') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -495,15 +506,21 @@ function showUninstallModal(id: string, pkg: PackageDataEntry) {
|
||||
}
|
||||
}
|
||||
|
||||
const uninstalling = ref(false)
|
||||
|
||||
async function confirmUninstall() {
|
||||
const { appId } = uninstallModal.value
|
||||
uninstallModal.value.show = false
|
||||
|
||||
uninstalling.value = true
|
||||
|
||||
try {
|
||||
await store.uninstallPackage(appId)
|
||||
uninstallModal.value.show = false
|
||||
} catch (err) {
|
||||
if (import.meta.env.DEV) console.error('Failed to uninstall app:', err)
|
||||
showActionError(`Failed to uninstall app: ${err instanceof Error ? err.message : 'Unknown error'}`)
|
||||
uninstallModal.value.show = false
|
||||
} finally {
|
||||
uninstalling.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -654,8 +654,7 @@ const WEB_ONLY_APP_URLS: Record<string, string> = {
|
||||
/** App ID to port-based URL for container apps */
|
||||
const APP_LAUNCH_URLS: Record<string, string> = {
|
||||
'bitcoin-knots': 'http://localhost:8334',
|
||||
'electrs': 'http://localhost:50002',
|
||||
'btcpay-server': 'http://localhost:23000',
|
||||
'btcpay-server': 'http://localhost:23000',
|
||||
'lnd': 'http://localhost:8081',
|
||||
'mempool': 'http://localhost:4080',
|
||||
'homeassistant': 'http://localhost:8123',
|
||||
@@ -732,7 +731,7 @@ async function loadCommunityMarketplace() {
|
||||
|
||||
// Get app tier classification (matches backend get_app_tier)
|
||||
function getAppTier(appId: string): string {
|
||||
const core = ['bitcoin-knots', 'bitcoin', 'lnd', 'mempool', 'electrs', 'btcpay-server', 'dwn', 'filebrowser']
|
||||
const core = ['bitcoin-knots', 'bitcoin', 'lnd', 'mempool', 'btcpay-server', 'dwn', 'filebrowser']
|
||||
const recommended = ['fedimint', 'vaultwarden', 'uptime-kuma', 'grafana', 'searxng', 'tailscale', 'portainer']
|
||||
if (core.includes(appId)) return 'core'
|
||||
if (recommended.includes(appId)) return 'recommended'
|
||||
@@ -753,17 +752,6 @@ function getCuratedAppList() {
|
||||
manifestUrl: undefined,
|
||||
repoUrl: 'https://github.com/bitcoinknots/bitcoin'
|
||||
},
|
||||
{
|
||||
id: 'electrs',
|
||||
title: 'Electrs',
|
||||
version: '0.4.1',
|
||||
description: 'Electrum protocol indexer for Bitcoin. Powers Mempool and other Electrum clients. Requires Bitcoin Knots or Bitcoin Core.',
|
||||
icon: '/assets/img/app-icons/electrs.svg',
|
||||
author: 'Roman Zeyde',
|
||||
dockerImage: 'docker.io/mempool/electrs:v0.4.1',
|
||||
manifestUrl: undefined,
|
||||
repoUrl: 'https://github.com/romanz/electrs'
|
||||
},
|
||||
{
|
||||
id: 'btcpay-server',
|
||||
title: 'BTCPay Server',
|
||||
|
||||
+20
-14
@@ -218,7 +218,7 @@
|
||||
<!-- Send Message Modal -->
|
||||
<Teleport to="body">
|
||||
<div v-if="showSendMessageModal" class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-md" @click.self="closeSendMessageModal()">
|
||||
<div ref="sendMessageModalRef" class="glass-card p-6 max-w-md w-full max-h-[90vh] overflow-y-auto">
|
||||
<div ref="sendMessageModalRef" class="glass-card p-6 max-w-2xl w-full max-h-[90vh] overflow-y-auto">
|
||||
<h3 class="text-lg font-semibold text-white mb-4">{{ t('web5.sendMessageTitle') }}</h3>
|
||||
<p class="text-white/70 text-sm mb-4">Messages are sent over the Tor network to the selected peer.</p>
|
||||
<div class="space-y-4">
|
||||
@@ -1034,7 +1034,7 @@
|
||||
<!-- Add Content Modal -->
|
||||
<Teleport to="body">
|
||||
<div v-if="showAddContentModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" @click.self="showAddContentModal = false" @keydown.escape="showAddContentModal = false">
|
||||
<div class="glass-card p-6 w-full max-w-md mx-4" role="dialog" aria-modal="true" aria-labelledby="add-content-title">
|
||||
<div class="glass-card p-6 w-full max-w-2xl mx-4 max-h-[90vh] overflow-y-auto" role="dialog" aria-modal="true" aria-labelledby="add-content-title">
|
||||
<h2 id="add-content-title" class="text-lg font-bold text-white mb-4">{{ t('web5.addContentTitle') }}</h2>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
@@ -1202,7 +1202,7 @@
|
||||
|
||||
<!-- Create Identity Modal -->
|
||||
<div v-if="showCreateIdentityModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" @click.self="showCreateIdentityModal = false" @keydown.escape="showCreateIdentityModal = false">
|
||||
<div class="glass-card p-6 w-full max-w-md mx-4" role="dialog" aria-modal="true" aria-labelledby="create-identity-title">
|
||||
<div class="glass-card p-6 w-full max-w-2xl mx-4 max-h-[90vh] overflow-y-auto" role="dialog" aria-modal="true" aria-labelledby="create-identity-title">
|
||||
<h2 id="create-identity-title" class="text-lg font-bold text-white mb-4">{{ t('web5.createIdentityTitle') }}</h2>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
@@ -1236,7 +1236,7 @@
|
||||
|
||||
<!-- Delete Confirmation Modal -->
|
||||
<div v-if="deleteIdentityTarget" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" @click.self="deleteIdentityTarget = null" @keydown.escape="deleteIdentityTarget = null">
|
||||
<div class="glass-card p-6 w-full max-w-sm mx-4" role="dialog" aria-modal="true" aria-labelledby="delete-identity-title">
|
||||
<div class="glass-card p-6 w-full max-w-2xl mx-4 max-h-[90vh] overflow-y-auto" role="dialog" aria-modal="true" aria-labelledby="delete-identity-title">
|
||||
<h2 id="delete-identity-title" class="text-lg font-bold text-white mb-2">{{ t('web5.deleteIdentityTitle') }}</h2>
|
||||
<p class="text-white/60 text-sm mb-4">{{ t('web5.deleteIdentityConfirm') }}</p>
|
||||
<div class="flex gap-3">
|
||||
@@ -1249,7 +1249,7 @@
|
||||
</div>
|
||||
<!-- Unified Send Modal -->
|
||||
<div v-if="showUnifiedSendModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" @click.self="closeUnifiedSendModal" @keydown.escape="closeUnifiedSendModal">
|
||||
<div class="glass-card p-6 w-full max-w-md mx-4" role="dialog" aria-modal="true" aria-labelledby="send-bitcoin-title">
|
||||
<div class="glass-card p-6 w-full max-w-2xl mx-4 max-h-[90vh] overflow-y-auto" role="dialog" aria-modal="true" aria-labelledby="send-bitcoin-title">
|
||||
<h2 id="send-bitcoin-title" class="text-lg font-bold text-white mb-4">{{ t('web5.sendBitcoinTitle') }}</h2>
|
||||
|
||||
<!-- Method tabs -->
|
||||
@@ -1349,7 +1349,7 @@
|
||||
|
||||
<!-- Unified Receive Modal -->
|
||||
<div v-if="showUnifiedReceiveModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" @click.self="closeUnifiedReceiveModal" @keydown.escape="closeUnifiedReceiveModal">
|
||||
<div class="glass-card p-6 w-full max-w-md mx-4" role="dialog" aria-modal="true" aria-labelledby="receive-bitcoin-title">
|
||||
<div class="glass-card p-6 w-full max-w-2xl mx-4 max-h-[90vh] overflow-y-auto" role="dialog" aria-modal="true" aria-labelledby="receive-bitcoin-title">
|
||||
<h2 id="receive-bitcoin-title" class="text-lg font-bold text-white mb-4">{{ t('web5.receiveBitcoinTitle') }}</h2>
|
||||
|
||||
<!-- Method tabs -->
|
||||
@@ -1642,7 +1642,7 @@
|
||||
|
||||
<!-- Domains Management Modal -->
|
||||
<div v-if="showDomainsModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" @click.self="showDomainsModal = false" @keydown.escape="showDomainsModal = false">
|
||||
<div class="glass-card p-6 w-full max-w-lg mx-4 max-h-[80vh] overflow-y-auto" role="dialog" aria-modal="true" aria-labelledby="domains-title">
|
||||
<div class="glass-card p-6 w-full max-w-2xl mx-4 max-h-[90vh] overflow-y-auto" role="dialog" aria-modal="true" aria-labelledby="domains-title">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 id="domains-title" class="text-lg font-bold text-white">{{ t('web5.domainsTitle') }}</h2>
|
||||
<button @click="showDomainsModal = false" class="text-white/40 hover:text-white/80 transition-colors">
|
||||
@@ -1719,7 +1719,7 @@
|
||||
|
||||
<!-- Relay Management Modal -->
|
||||
<div v-if="showRelaysModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" @click.self="showRelaysModal = false" @keydown.escape="showRelaysModal = false">
|
||||
<div class="glass-card p-6 w-full max-w-lg mx-4 max-h-[80vh] overflow-y-auto" role="dialog" aria-modal="true" aria-labelledby="relays-title">
|
||||
<div class="glass-card p-6 w-full max-w-2xl mx-4 max-h-[90vh] overflow-y-auto" role="dialog" aria-modal="true" aria-labelledby="relays-title">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 id="relays-title" class="text-lg font-bold text-white">{{ t('web5.nostrRelays') }}</h2>
|
||||
<button @click="showRelaysModal = false" class="text-white/40 hover:text-white/80 transition-colors">
|
||||
@@ -2035,8 +2035,14 @@ async function publishDhtDid() {
|
||||
})
|
||||
dhtDid.value = res.dht_did
|
||||
localStorage.setItem('neode_dht_did', res.dht_did)
|
||||
} catch (e) {
|
||||
console.error('DHT publish failed:', e)
|
||||
} catch {
|
||||
// identity.create-dht-did not yet implemented — generate placeholder
|
||||
const did = storedDid.value || localStorage.getItem('neode_did')
|
||||
if (did) {
|
||||
const dhtVersion = did.replace('did:key:', 'did:dht:')
|
||||
dhtDid.value = dhtVersion
|
||||
localStorage.setItem('neode_dht_did', dhtVersion)
|
||||
}
|
||||
} finally {
|
||||
publishingDht.value = false
|
||||
}
|
||||
@@ -2049,8 +2055,8 @@ async function refreshDhtDid() {
|
||||
const defaultId = identities.identities?.find((i: { is_default: boolean }) => i.is_default)
|
||||
if (!defaultId) return
|
||||
await rpcClient.call({ method: 'identity.refresh-dht-did', params: { identity_id: defaultId.id } })
|
||||
} catch (e) {
|
||||
console.error('DHT refresh failed:', e)
|
||||
} catch {
|
||||
// identity.refresh-dht-did not yet implemented — silently ignore
|
||||
} finally {
|
||||
publishingDht.value = false
|
||||
}
|
||||
@@ -2504,7 +2510,7 @@ async function finalizePsbt() {
|
||||
|
||||
function copyPsbt() {
|
||||
if (!psbtData.value) return
|
||||
window.safeClipboardWrite(psbtData.value)
|
||||
safeClipboardWrite(psbtData.value)
|
||||
unifiedSendError.value = t('web5.psbtCopied')
|
||||
}
|
||||
|
||||
@@ -2577,7 +2583,7 @@ function copyEcashToken(token: string) {
|
||||
|
||||
async function safeClipboardWrite(text: string): Promise<void> {
|
||||
if (navigator.clipboard?.writeText) {
|
||||
await safeClipboardWrite(text)
|
||||
await navigator.clipboard.writeText(text)
|
||||
} else {
|
||||
const ta = document.createElement('textarea')
|
||||
ta.value = text
|
||||
|
||||
Reference in New Issue
Block a user