feat(gateway): enhance gateway process management with auto-reconnection

Improve Gateway lifecycle management with the following features:

- Add exponential backoff reconnection (1s-30s delay, max 10 attempts)
- Add health check monitoring every 30 seconds
- Add proper restart method with graceful shutdown
- Handle server-initiated notifications (channel status, chat messages)
- Add 'reconnecting' state for better UI feedback
- Enhance IPC handlers with isConnected and health check endpoints
- Update preload script with new event channels
- Improve type safety and error handling throughout

Also fixes several TypeScript errors and unused variable warnings.
This commit is contained in:
Haze
2026-02-05 23:15:07 +08:00
Unverified
parent b8ab0208d0
commit 1646536e40
15 changed files with 601 additions and 74 deletions

View File

@@ -1,20 +1,31 @@
/**
* Gateway State Store
* Manages Gateway connection state
* Manages Gateway connection state and communication
*/
import { create } from 'zustand';
import type { GatewayStatus } from '../types/gateway';
interface GatewayHealth {
ok: boolean;
error?: string;
uptime?: number;
}
interface GatewayState {
status: GatewayStatus;
health: GatewayHealth | null;
isInitialized: boolean;
lastError: string | null;
// Actions
init: () => Promise<void>;
start: () => Promise<void>;
stop: () => Promise<void>;
restart: () => Promise<void>;
checkHealth: () => Promise<GatewayHealth>;
rpc: <T>(method: string, params?: unknown, timeoutMs?: number) => Promise<T>;
setStatus: (status: GatewayStatus) => void;
clearError: () => void;
}
export const useGatewayStore = create<GatewayState>((set, get) => ({
@@ -22,7 +33,9 @@ export const useGatewayStore = create<GatewayState>((set, get) => ({
state: 'stopped',
port: 18789,
},
health: null,
isInitialized: false,
lastError: null,
init: async () => {
if (get().isInitialized) return;
@@ -36,38 +49,111 @@ export const useGatewayStore = create<GatewayState>((set, get) => ({
window.electron.ipcRenderer.on('gateway:status-changed', (newStatus) => {
set({ status: newStatus as GatewayStatus });
});
// Listen for errors
window.electron.ipcRenderer.on('gateway:error', (error) => {
set({ lastError: String(error) });
});
// Listen for notifications
window.electron.ipcRenderer.on('gateway:notification', (notification) => {
console.log('Gateway notification:', notification);
// Could dispatch to other stores based on notification type
});
} catch (error) {
console.error('Failed to initialize Gateway:', error);
set({ lastError: String(error) });
}
},
start: async () => {
try {
set({ status: { ...get().status, state: 'starting' } });
set({ status: { ...get().status, state: 'starting' }, lastError: null });
const result = await window.electron.ipcRenderer.invoke('gateway:start') as { success: boolean; error?: string };
if (!result.success) {
set({ status: { ...get().status, state: 'error', error: result.error } });
set({
status: { ...get().status, state: 'error', error: result.error },
lastError: result.error || 'Failed to start Gateway'
});
}
} catch (error) {
set({ status: { ...get().status, state: 'error', error: String(error) } });
set({
status: { ...get().status, state: 'error', error: String(error) },
lastError: String(error)
});
}
},
stop: async () => {
try {
await window.electron.ipcRenderer.invoke('gateway:stop');
set({ status: { ...get().status, state: 'stopped' } });
set({ status: { ...get().status, state: 'stopped' }, lastError: null });
} catch (error) {
console.error('Failed to stop Gateway:', error);
set({ lastError: String(error) });
}
},
restart: async () => {
const { stop, start } = get();
await stop();
await start();
try {
set({ status: { ...get().status, state: 'starting' }, lastError: null });
const result = await window.electron.ipcRenderer.invoke('gateway:restart') as { success: boolean; error?: string };
if (!result.success) {
set({
status: { ...get().status, state: 'error', error: result.error },
lastError: result.error || 'Failed to restart Gateway'
});
}
} catch (error) {
set({
status: { ...get().status, state: 'error', error: String(error) },
lastError: String(error)
});
}
},
checkHealth: async () => {
try {
const result = await window.electron.ipcRenderer.invoke('gateway:health') as {
success: boolean;
ok: boolean;
error?: string;
uptime?: number
};
const health: GatewayHealth = {
ok: result.ok,
error: result.error,
uptime: result.uptime,
};
set({ health });
return health;
} catch (error) {
const health: GatewayHealth = { ok: false, error: String(error) };
set({ health });
return health;
}
},
rpc: async <T>(method: string, params?: unknown, timeoutMs?: number): Promise<T> => {
const result = await window.electron.ipcRenderer.invoke('gateway:rpc', method, params, timeoutMs) as {
success: boolean;
result?: T;
error?: string;
};
if (!result.success) {
throw new Error(result.error || `RPC call failed: ${method}`);
}
return result.result as T;
},
setStatus: (status) => set({ status }),
clearError: () => set({ lastError: null }),
}));