bug fixing and deploy and build diagnostics
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
// Sync store — WebSocket connection, real-time data, patch application
|
||||
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import type { DataModel } from '../types/api'
|
||||
import { wsClient, applyDataPatch } from '../api/websocket'
|
||||
import { rpcClient } from '../api/rpc-client'
|
||||
|
||||
export const useSyncStore = defineStore('sync', () => {
|
||||
// State
|
||||
const data = ref<DataModel | null>(null)
|
||||
const isConnected = ref(false)
|
||||
const isReconnecting = ref(false)
|
||||
let isWsSubscribed = false
|
||||
let isWsConnecting = false
|
||||
|
||||
// Computed
|
||||
const serverInfo = computed(() => data.value?.['server-info'])
|
||||
const packages = computed(() => data.value?.['package-data'] || {})
|
||||
const peerHealth = computed<Record<string, boolean>>(() => data.value?.['peer-health'] || {})
|
||||
const uiData = computed(() => data.value?.ui)
|
||||
|
||||
// Actions
|
||||
async function connectWebSocket(): Promise<void> {
|
||||
// Prevent concurrent connection attempts
|
||||
if (isWsConnecting) return
|
||||
isWsConnecting = true
|
||||
|
||||
try {
|
||||
if (import.meta.env.DEV) console.log('[Store] Connecting WebSocket...')
|
||||
isReconnecting.value = true
|
||||
|
||||
// Don't create multiple subscriptions - check if already subscribed
|
||||
if (!isWsSubscribed) {
|
||||
// Subscribe to updates BEFORE connecting (so we catch initial data)
|
||||
isWsSubscribed = true
|
||||
|
||||
// Listen for connection state changes
|
||||
wsClient.onConnectionStateChange((state) => {
|
||||
if (import.meta.env.DEV) console.log('[Store] WebSocket connection state changed:', state)
|
||||
isConnected.value = state === 'connected'
|
||||
isReconnecting.value = state === 'connecting'
|
||||
})
|
||||
|
||||
wsClient.subscribe((update: { type?: string; data?: DataModel; rev?: number; patch?: import('@/types/api').PatchOperation[] }) => {
|
||||
// Handle mock backend format: {type: 'initial', data: {...}}
|
||||
if (update?.type === 'initial' && update?.data) {
|
||||
if (import.meta.env.DEV) console.log('[Store] Received initial data from mock backend')
|
||||
data.value = update.data
|
||||
isConnected.value = true
|
||||
isReconnecting.value = false
|
||||
}
|
||||
// Handle real backend format: {rev: 0, data: {...}}
|
||||
else if (update?.data && update?.rev !== undefined) {
|
||||
data.value = update.data
|
||||
isConnected.value = true
|
||||
isReconnecting.value = false
|
||||
}
|
||||
// Handle patch updates (both backends)
|
||||
else if (data.value && update?.patch) {
|
||||
try {
|
||||
if (import.meta.env.DEV) console.log('[Store] Applying patch at revision', update.rev || 'unknown')
|
||||
data.value = applyDataPatch(data.value, update.patch)
|
||||
// Mark as connected once we receive any valid patch
|
||||
if (!isConnected.value) {
|
||||
isConnected.value = true
|
||||
isReconnecting.value = false
|
||||
}
|
||||
} catch (err) {
|
||||
if (import.meta.env.DEV) console.error('[Store] Failed to apply WebSocket patch:', err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Now connect (or reconnect if already connected)
|
||||
// Only attempt to connect if not already connected
|
||||
if (wsClient.isConnected()) {
|
||||
if (import.meta.env.DEV) console.log('[Store] WebSocket already connected')
|
||||
isConnected.value = true
|
||||
isReconnecting.value = false
|
||||
return
|
||||
}
|
||||
|
||||
await wsClient.connect()
|
||||
if (import.meta.env.DEV) console.log('[Store] WebSocket connected')
|
||||
|
||||
// Fetch fresh state after reconnect to avoid stale patch application
|
||||
try {
|
||||
const freshState = await rpcClient.call<{ data: DataModel }>({ method: 'server.get-state' })
|
||||
if (freshState?.data) {
|
||||
data.value = freshState.data
|
||||
}
|
||||
} catch {
|
||||
// Non-fatal: WebSocket patches will still work
|
||||
if (import.meta.env.DEV) console.warn('[Store] Failed to fetch fresh state after reconnect')
|
||||
}
|
||||
|
||||
// Connection state will be updated via the callback
|
||||
if (wsClient.isConnected()) {
|
||||
isConnected.value = true
|
||||
isReconnecting.value = false
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
if (import.meta.env.DEV) console.error('[Store] WebSocket connection failed:', err)
|
||||
// Don't mark as disconnected immediately - let reconnection logic handle it
|
||||
// The WebSocket client will retry automatically
|
||||
isReconnecting.value = true
|
||||
isConnected.value = false
|
||||
// Don't throw - allow app to work without real-time updates
|
||||
// The WebSocket will reconnect in the background
|
||||
} finally {
|
||||
isWsConnecting = false
|
||||
}
|
||||
}
|
||||
|
||||
async function initializeData(): Promise<void> {
|
||||
// Initialize with empty data structure
|
||||
// The WebSocket will populate it with real data
|
||||
data.value = {
|
||||
'server-info': {
|
||||
id: '',
|
||||
version: '',
|
||||
name: null,
|
||||
pubkey: '',
|
||||
'status-info': {
|
||||
restarting: false,
|
||||
'shutting-down': false,
|
||||
updated: false,
|
||||
'backup-progress': null,
|
||||
'update-progress': null,
|
||||
},
|
||||
'lan-address': null,
|
||||
'tor-address': null,
|
||||
unread: 0,
|
||||
'wifi-ssids': [],
|
||||
'zram-enabled': false,
|
||||
},
|
||||
'package-data': {},
|
||||
ui: {
|
||||
name: null,
|
||||
'ack-welcome': '',
|
||||
marketplace: {
|
||||
'selected-hosts': [],
|
||||
'known-hosts': {},
|
||||
},
|
||||
theme: 'dark',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/** Reset sync state on logout — called by auth store */
|
||||
function resetOnLogout(): void {
|
||||
data.value = null
|
||||
isWsSubscribed = false
|
||||
wsClient.disconnect()
|
||||
isConnected.value = false
|
||||
isReconnecting.value = false
|
||||
}
|
||||
|
||||
return {
|
||||
// State
|
||||
data,
|
||||
isConnected,
|
||||
isReconnecting,
|
||||
|
||||
// Computed
|
||||
serverInfo,
|
||||
packages,
|
||||
peerHealth,
|
||||
uiData,
|
||||
|
||||
// Actions
|
||||
connectWebSocket,
|
||||
initializeData,
|
||||
resetOnLogout,
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user