Improve failover startup handling and status UI
This commit is contained in:
@@ -6,6 +6,25 @@ import {
|
||||
} from '../singbox.js';
|
||||
|
||||
type Role = 'primary' | 'reserve';
|
||||
const SELECTOR_READY_ATTEMPTS = 20;
|
||||
const SELECTOR_READY_DELAY_MS = 100;
|
||||
|
||||
const transientStartupError = (error: unknown) => (
|
||||
error && typeof error === 'object' && 'code' in error
|
||||
? ['ECONNREFUSED', 'ECONNRESET'].includes(String(error.code))
|
||||
: false
|
||||
);
|
||||
|
||||
async function whenReady<T>(operation: () => Promise<T>): Promise<T> {
|
||||
for (let attempt = 1; ; attempt += 1) {
|
||||
try {
|
||||
return await operation();
|
||||
} catch (error) {
|
||||
if (!transientStartupError(error) || attempt === SELECTOR_READY_ATTEMPTS) throw error;
|
||||
await new Promise((resolve) => setTimeout(resolve, SELECTOR_READY_DELAY_MS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function request(port: number, method: string, body?: unknown): Promise<unknown> {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -51,13 +70,13 @@ export function createSingboxSelectorService({
|
||||
send?: (method: string, body?: unknown) => Promise<unknown>;
|
||||
}) {
|
||||
async function read() {
|
||||
const value = await send('GET') as Record<string, unknown>;
|
||||
const value = await whenReady(() => send('GET')) as Record<string, unknown>;
|
||||
const role = roleFor(value.now);
|
||||
if (!role) throw new Error('Sing-box selector вернул неизвестный outbound');
|
||||
return { role };
|
||||
}
|
||||
async function select(role: Role) {
|
||||
await send('PUT', { name: tagFor(role) });
|
||||
await whenReady(() => send('PUT', { name: tagFor(role) }));
|
||||
const selected = await read();
|
||||
if (selected.role !== role) throw new Error('Sing-box selector не подтвердил переключение');
|
||||
return selected;
|
||||
|
||||
Reference in New Issue
Block a user