Files
MikrotikManager/lib/server-operations.ts

50 lines
1.9 KiB
TypeScript

/** A synchronous lock: React state alone cannot prevent two calls in one tick. */
export class OperationGate {
private keys = new Set<string>()
acquire(key: string): boolean {
if (this.keys.has(key)) return false
this.keys.add(key)
return true
}
release(key: string) { this.keys.delete(key) }
has(key: string) { return this.keys.has(key) }
}
export type PollResult = { id: string; error: string | null }
export function serverOperationError(error: unknown): string {
if (error && typeof error === "object" && "status" in error && error.status === 403) {
return "Недостаточно прав для этого действия."
}
if (error instanceof Error && (error.name === "TimeoutError" || error.name === "AbortError")) {
return "Время ожидания истекло. Результат операции неизвестен — обновите данные перед повтором."
}
if (error instanceof TypeError) return "Нет соединения с сервером. Проверьте подключение и обновите данные."
return error instanceof Error ? error.message : "Не удалось выполнить действие. Попробуйте ещё раз."
}
/** Bounded polling, preserving one outcome for every requested server. */
export async function pollServers(
ids: string[],
poll: (id: string) => Promise<void>,
): Promise<PollResult[]> {
const uniqueIds = [...new Set(ids)]
const results: PollResult[] = new Array(uniqueIds.length)
let next = 0
await Promise.all(Array.from({ length: Math.min(4, uniqueIds.length) }, async () => {
while (next < uniqueIds.length) {
const index = next++
const id = uniqueIds[index]
try {
await poll(id)
results[index] = { id, error: null }
} catch (error) {
results[index] = { id, error: serverOperationError(error) }
}
}
}))
return results
}