refactor(channels): integrate channel runtime status management and enhance account status handling (#547)

This commit is contained in:
Haze
2026-03-17 11:29:17 +08:00
committed by GitHub
Unverified
parent 43fe7a4d1c
commit d4367d3265
10 changed files with 713 additions and 61 deletions

View File

@@ -4,6 +4,11 @@
*/
import { create } from 'zustand';
import { hostApiFetch } from '@/lib/host-api';
import {
isChannelRuntimeConnected,
pickChannelRuntimeStatus,
type ChannelRuntimeAccountSnapshot,
} from '@/lib/channel-status';
import { useGatewayStore } from './gateway';
import { CHANNEL_NAMES, type Channel, type ChannelType } from '../types/channel';
@@ -52,6 +57,10 @@ export const useChannelsStore = create<ChannelsState>((set, get) => ({
lastConnectedAt?: number | null;
lastInboundAt?: number | null;
lastOutboundAt?: number | null;
lastProbeAt?: number | null;
probe?: {
ok?: boolean;
} | null;
}>>;
channelDefaultAccountId?: Record<string, string>;
}>('channels.status', { probe: true });
@@ -72,39 +81,19 @@ export const useChannelsStore = create<ChannelsState>((set, get) => ({
const accounts = data.channelAccounts?.[channelId] || [];
const defaultAccountId = data.channelDefaultAccountId?.[channelId];
const summarySignal = summary as { error?: string; lastError?: string } | undefined;
const primaryAccount =
(defaultAccountId ? accounts.find((a) => a.accountId === defaultAccountId) : undefined) ||
accounts.find((a) => a.connected === true || a.linked === true) ||
accounts.find((a) => isChannelRuntimeConnected(a as ChannelRuntimeAccountSnapshot)) ||
accounts[0];
// Map gateway status to our status format
let status: Channel['status'] = 'disconnected';
const now = Date.now();
const RECENT_MS = 10 * 60 * 1000;
const hasRecentActivity = (a: { lastInboundAt?: number | null; lastOutboundAt?: number | null; lastConnectedAt?: number | null }) =>
(typeof a.lastInboundAt === 'number' && now - a.lastInboundAt < RECENT_MS) ||
(typeof a.lastOutboundAt === 'number' && now - a.lastOutboundAt < RECENT_MS) ||
(typeof a.lastConnectedAt === 'number' && now - a.lastConnectedAt < RECENT_MS);
const anyConnected = accounts.some((a) => a.connected === true || a.linked === true || hasRecentActivity(a));
const anyRunning = accounts.some((a) => a.running === true);
const status: Channel['status'] = pickChannelRuntimeStatus(accounts, summarySignal);
const summaryError =
typeof (summary as { error?: string })?.error === 'string'
? (summary as { error?: string }).error
: typeof (summary as { lastError?: string })?.lastError === 'string'
? (summary as { lastError?: string }).lastError
typeof summarySignal?.error === 'string'
? summarySignal.error
: typeof summarySignal?.lastError === 'string'
? summarySignal.lastError
: undefined;
const anyError =
accounts.some((a) => typeof a.lastError === 'string' && a.lastError) || Boolean(summaryError);
if (anyConnected) {
status = 'connected';
} else if (anyRunning && !anyError) {
status = 'connected';
} else if (anyError) {
status = 'error';
} else if (anyRunning) {
status = 'connecting';
}
channels.push({
id: `${channelId}-${primaryAccount?.accountId || 'default'}`,