fix: WebSocket race conditions, Vue error handler, remove sudo podman, add container health checks

- F1: Guard connectWebSocket against concurrent calls with isWsConnecting flag
- F2: Serialize mesh send operations with sendQueue to prevent fetchMessages races
- F3: Add global Vue error handler with toast notification
- S1: Replace sudo podman with podman across all scripts (rootless Podman)
- S2: Add health-cmd to all 40 container run commands in first-boot-containers.sh

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-21 01:11:05 +00:00
co-authored by Claude Opus 4.6
parent c299199d37
commit 38dc845f57
10 changed files with 301 additions and 110 deletions
+25 -16
View File
@@ -118,6 +118,9 @@ export const useMeshStore = defineStore('mesh', () => {
const error = ref<string | null>(null)
const sending = ref(false)
// Serialize send operations to prevent concurrent fetchMessages() races
let sendQueue: Promise<void> = Promise.resolve()
// Node position tracking for map view (contact_id -> position)
const nodePositions = ref<Map<number, NodePosition>>(new Map())
@@ -247,24 +250,30 @@ export const useMeshStore = defineStore('mesh', () => {
}
async function sendMessage(contactId: number, message: string) {
try {
sending.value = true
error.value = null
const res = await rpcClient.call<{ sent: boolean; message_id: number; encrypted: boolean }>({
method: 'mesh.send',
params: { contact_id: contactId, message: message.trim() },
})
// Refresh messages after sending
if (res.sent) {
await fetchMessages()
const doSend = async () => {
try {
sending.value = true
error.value = null
const res = await rpcClient.call<{ sent: boolean; message_id: number; encrypted: boolean }>({
method: 'mesh.send',
params: { contact_id: contactId, message: message.trim() },
})
// Refresh messages after sending
if (res.sent) {
await fetchMessages()
}
return res
} catch (err: unknown) {
error.value = err instanceof Error ? err.message : 'Failed to send mesh message'
throw err
} finally {
sending.value = false
}
return res
} catch (err: unknown) {
error.value = err instanceof Error ? err.message : 'Failed to send mesh message'
throw err
} finally {
sending.value = false
}
// Chain onto send queue to prevent concurrent fetchMessages() calls
const result = sendQueue.then(doSend, doSend)
sendQueue = result.then(() => {}, () => {})
return result
}
async function broadcastIdentity() {