fix: BUG-1 CSRF, TASK-8 H2/H3/H4, BUG-20/37/40/41 — 7 bugs fixed
BUG-1 (P0): CSRF tokens now HMAC-derived from session token instead of
random — survives backend restarts, eliminates cookie/header race conditions.
Frontend retries 403s as belt-and-suspenders.
TASK-8 H2: federation.peer-joined verifies ed25519 signature on join messages.
TASK-8 H3: federation.peer-address-changed requires signed proof from known peer.
TASK-8 H4: Rust backend default bind 0.0.0.0 → 127.0.0.1 (nginx proxies all).
BUG-20: ElectrumX index estimate string fixed from ~55GB to ~130GB.
BUG-37: App card Start/Stop buttons split into loading vs interactive states
to prevent WebSocket state flicker during container scans.
BUG-40: Uninstall modal uses Teleport to body with z-[3000] for full overlay.
BUG-41: Uninstalling overlay on card + optimistic store removal.
Updated MASTER_PLAN.md and BETA-PROGRESS.md to reflect all completed work.
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
00bfd62393
commit
41ff1021ad
+32
-20
@@ -249,17 +249,25 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<!-- Incoming Transactions Badge -->
|
||||
<!-- Transactions button — switches to incoming state when pending txs exist -->
|
||||
<button
|
||||
v-if="incomingTxCount > 0"
|
||||
@click="showIncomingTxPanel = !showIncomingTxPanel"
|
||||
class="incoming-tx-badge shrink-0"
|
||||
@click="incomingTxCount > 0 ? showIncomingTxPanel = !showIncomingTxPanel : showTransactionsModal = true"
|
||||
:class="incomingTxCount > 0 ? 'incoming-tx-badge' : 'text-white/50 hover:text-white/80 text-xs px-2 py-1 rounded-lg bg-white/5 hover:bg-white/10 transition-colors'"
|
||||
class="shrink-0"
|
||||
>
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 14l-7 7m0 0l-7-7m7 7V3" />
|
||||
</svg>
|
||||
<span>Incoming {{ incomingTxCount }}</span>
|
||||
<span class="incoming-tx-ping"></span>
|
||||
<template v-if="incomingTxCount > 0">
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 14l-7 7m0 0l-7-7m7 7V3" />
|
||||
</svg>
|
||||
<span>Incoming {{ incomingTxCount }}</span>
|
||||
<span class="incoming-tx-ping"></span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<svg class="w-3.5 h-3.5 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4" />
|
||||
</svg>
|
||||
<span>Transactions</span>
|
||||
</template>
|
||||
</button>
|
||||
<RouterLink to="/dashboard/web5" :aria-label="t('home.goToWeb5')" class="text-white/60 hover:text-white transition-colors">
|
||||
<svg class="w-5 h-5" aria-hidden="true" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -345,19 +353,19 @@
|
||||
<span class="text-purple-400 text-sm font-medium">{{ walletEcash.toLocaleString() }} sats</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-card-buttons grid grid-cols-4 gap-2 mt-auto pt-4 shrink-0">
|
||||
<div class="home-card-buttons grid gap-2 mt-auto pt-4 shrink-0" :class="isDev ? 'grid-cols-4' : 'grid-cols-3'">
|
||||
<button @click="showSendModal = true" class="home-card-btn px-3 py-2 glass-button rounded-lg text-sm font-medium text-center transition-colors">
|
||||
{{ t('common.send') }}
|
||||
</button>
|
||||
<button @click="showReceiveModal = true" class="home-card-btn px-3 py-2 glass-button rounded-lg text-sm font-medium text-center transition-colors">
|
||||
{{ t('web5.receiveBitcoin') }}
|
||||
</button>
|
||||
<button @click="devFaucet" class="home-card-btn px-3 py-2 glass-button rounded-lg text-sm font-medium text-center transition-colors text-green-400">
|
||||
<button @click="showTransactionsModal = true" class="home-card-btn px-3 py-2 glass-button rounded-lg text-sm font-medium text-center transition-colors">
|
||||
Transactions
|
||||
</button>
|
||||
<button v-if="isDev" @click="devFaucet" class="home-card-btn px-3 py-2 glass-button rounded-lg text-sm font-medium text-center transition-colors text-green-400">
|
||||
Faucet
|
||||
</button>
|
||||
<RouterLink to="/dashboard/web5" class="home-card-btn px-3 py-2 glass-button rounded-lg text-sm font-medium text-center transition-colors">
|
||||
Web5
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -397,7 +405,7 @@
|
||||
<div class="p-4 bg-white/5 rounded-lg">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<p class="text-xs text-white/60">{{ t('home.cpu') }}</p>
|
||||
<p class="text-sm font-medium" :class="gaugeTextColor(systemStats.cpuPercent)">{{ systemStats.cpuPercent.toFixed(0) }}%</p>
|
||||
<p class="text-sm font-medium" :class="gaugeTextColor(systemStats.cpuPercent)">{{ (systemStats.cpuPercent ?? 0).toFixed(0) }}%</p>
|
||||
</div>
|
||||
<div class="w-full h-2 bg-white/10 rounded-full overflow-hidden">
|
||||
<div class="h-full rounded-full transition-all duration-500" :class="gaugeBarColor(systemStats.cpuPercent)" :style="{ width: systemStats.cpuPercent + '%' }"></div>
|
||||
@@ -477,9 +485,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Send/Receive Bitcoin Modals -->
|
||||
<!-- Wallet Modals -->
|
||||
<SendBitcoinModal :show="showSendModal" @close="showSendModal = false" @sent="loadWeb5Status()" />
|
||||
<ReceiveBitcoinModal :show="showReceiveModal" @close="showReceiveModal = false" @received="loadWeb5Status()" />
|
||||
<TransactionsModal :show="showTransactionsModal" :transactions="walletTransactions" @close="showTransactionsModal = false" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -488,6 +497,7 @@ import { RouterLink, useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import SendBitcoinModal from '@/components/SendBitcoinModal.vue'
|
||||
import ReceiveBitcoinModal from '@/components/ReceiveBitcoinModal.vue'
|
||||
import TransactionsModal from '@/components/TransactionsModal.vue'
|
||||
import { useAppStore } from '../stores/app'
|
||||
|
||||
const { t } = useI18n()
|
||||
@@ -503,6 +513,7 @@ import { rpcClient } from '@/api/rpc-client'
|
||||
|
||||
const router = useRouter()
|
||||
const uiMode = useUIModeStore()
|
||||
const isDev = import.meta.env.DEV
|
||||
const homeTab = ref<'dashboard' | 'setup'>('dashboard')
|
||||
const topGoals = GOALS.slice(0, 3)
|
||||
|
||||
@@ -718,18 +729,19 @@ onMounted(async () => {
|
||||
loadWeb5Status()
|
||||
})
|
||||
|
||||
// Send/Receive modals
|
||||
// Wallet modals
|
||||
const showSendModal = ref(false)
|
||||
const showReceiveModal = ref(false)
|
||||
const showTransactionsModal = ref(false)
|
||||
|
||||
// Dev faucet — adds mock funds to all wallet types
|
||||
// Dev faucet — adds mock funds to all wallet types (dev mode only)
|
||||
async function devFaucet() {
|
||||
try {
|
||||
const res = await rpcClient.call<{ message: string }>({ method: 'dev.faucet', params: { amount_sats: 1_000_000 } })
|
||||
console.log('[Faucet]', res.message)
|
||||
if (import.meta.env.DEV) console.log('[Faucet]', res.message)
|
||||
await loadWeb5Status()
|
||||
} catch (err) {
|
||||
console.error('[Faucet] Error:', err)
|
||||
if (import.meta.env.DEV) console.error('[Faucet] Error:', err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user