Initial commit MKD fixes
This commit is contained in:
206
services/connectionService.ts
Executable file
206
services/connectionService.ts
Executable file
@@ -0,0 +1,206 @@
|
||||
// Сервис управления подключением к серверу
|
||||
export type ConnectionStatus = 'connected' | 'connecting' | 'disconnected';
|
||||
|
||||
type ConnectionStatusCallback = (status: ConnectionStatus) => void;
|
||||
|
||||
class ConnectionService {
|
||||
private status: ConnectionStatus = 'disconnected';
|
||||
private listeners: Set<ConnectionStatusCallback> = new Set();
|
||||
private reconnectInterval: number | null = null;
|
||||
private reconnectAttempts = 0;
|
||||
private maxReconnectAttempts = Infinity; // Бесконечные попытки
|
||||
private reconnectDelay = 3000; // 3 секунды между попытками
|
||||
private checkInterval: number | null = null;
|
||||
private apiBaseUrl: string;
|
||||
|
||||
constructor() {
|
||||
this.apiBaseUrl = import.meta.env.VITE_API_BASE_URL || '';
|
||||
|
||||
// Если API не настроен, сразу устанавливаем статус connected
|
||||
if (!this.apiBaseUrl) {
|
||||
this.setStatus('connected');
|
||||
return;
|
||||
}
|
||||
|
||||
this.startConnectionCheck();
|
||||
this.initializeConnection();
|
||||
}
|
||||
|
||||
// Инициализация подключения
|
||||
private async initializeConnection() {
|
||||
if (!this.apiBaseUrl) {
|
||||
// Если API не настроен, считаем что подключение не требуется (используется localStorage)
|
||||
this.setStatus('connected');
|
||||
return;
|
||||
}
|
||||
|
||||
this.setStatus('connecting');
|
||||
await this.checkConnection();
|
||||
}
|
||||
|
||||
// Проверка подключения
|
||||
private async checkConnection(): Promise<boolean> {
|
||||
if (!this.apiBaseUrl) {
|
||||
this.setStatus('connected');
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 3000); // 3 секунды таймаут
|
||||
|
||||
// Пробуем несколько endpoints для проверки подключения
|
||||
const endpoints = ['/health', '/districts', '/buildings'];
|
||||
let connected = false;
|
||||
let lastError: Error | null = null;
|
||||
|
||||
for (const endpoint of endpoints) {
|
||||
try {
|
||||
const response = await fetch(`${this.apiBaseUrl}${endpoint}`, {
|
||||
method: 'GET',
|
||||
signal: controller.signal,
|
||||
cache: 'no-cache',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// Если получили ответ (даже с ошибкой 404, 403 и т.д.), сервер доступен
|
||||
// 404 означает, что сервер работает, но endpoint не найден - это нормально
|
||||
if (response.status < 500) {
|
||||
connected = true;
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
lastError = e instanceof Error ? e : new Error(String(e));
|
||||
// Пробуем следующий endpoint
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (connected) {
|
||||
this.setStatus('connected');
|
||||
this.reconnectAttempts = 0;
|
||||
this.stopReconnect();
|
||||
return true;
|
||||
} else {
|
||||
// Проверяем, не была ли это ошибка сети (CORS, таймаут и т.д.)
|
||||
const isNetworkError = lastError && (
|
||||
lastError.name === 'AbortError' ||
|
||||
lastError.message.includes('Failed to fetch') ||
|
||||
lastError.message.includes('NetworkError') ||
|
||||
lastError.message.includes('CORS')
|
||||
);
|
||||
|
||||
if (isNetworkError) {
|
||||
this.setStatus('disconnected');
|
||||
this.startReconnect();
|
||||
return false;
|
||||
} else {
|
||||
// Если это не ошибка сети, возможно сервер работает, но endpoint недоступен
|
||||
// В этом случае считаем что подключение есть
|
||||
this.setStatus('connected');
|
||||
this.stopReconnect();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Общая ошибка - считаем что нет подключения
|
||||
this.setStatus('disconnected');
|
||||
this.startReconnect();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Установка статуса и уведомление слушателей
|
||||
private setStatus(newStatus: ConnectionStatus) {
|
||||
if (this.status !== newStatus) {
|
||||
this.status = newStatus;
|
||||
this.listeners.forEach(callback => callback(newStatus));
|
||||
}
|
||||
}
|
||||
|
||||
// Начало процесса переподключения
|
||||
private startReconnect() {
|
||||
if (this.reconnectInterval) {
|
||||
return; // Уже переподключаемся
|
||||
}
|
||||
|
||||
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
||||
return; // Достигнут лимит попыток
|
||||
}
|
||||
|
||||
this.reconnectInterval = window.setInterval(async () => {
|
||||
this.reconnectAttempts++;
|
||||
this.setStatus('connecting');
|
||||
|
||||
const connected = await this.checkConnection();
|
||||
|
||||
if (connected) {
|
||||
this.stopReconnect();
|
||||
}
|
||||
}, this.reconnectDelay);
|
||||
}
|
||||
|
||||
// Остановка процесса переподключения
|
||||
private stopReconnect() {
|
||||
if (this.reconnectInterval) {
|
||||
clearInterval(this.reconnectInterval);
|
||||
this.reconnectInterval = null;
|
||||
}
|
||||
this.reconnectAttempts = 0;
|
||||
}
|
||||
|
||||
// Периодическая проверка подключения
|
||||
private startConnectionCheck() {
|
||||
// Проверяем каждые 10 секунд
|
||||
this.checkInterval = window.setInterval(async () => {
|
||||
if (this.status === 'connected') {
|
||||
await this.checkConnection();
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
// Получить текущий статус
|
||||
getStatus(): ConnectionStatus {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
// Подписаться на изменения статуса
|
||||
subscribe(callback: ConnectionStatusCallback): () => void {
|
||||
this.listeners.add(callback);
|
||||
// Сразу вызываем callback с текущим статусом
|
||||
callback(this.status);
|
||||
|
||||
// Возвращаем функцию отписки
|
||||
return () => {
|
||||
this.listeners.delete(callback);
|
||||
};
|
||||
}
|
||||
|
||||
// Принудительная проверка подключения
|
||||
async forceCheck(): Promise<boolean> {
|
||||
if (!this.apiBaseUrl) {
|
||||
// Если API не настроен, считаем что подключение не требуется
|
||||
this.setStatus('connected');
|
||||
return true;
|
||||
}
|
||||
this.setStatus('connecting');
|
||||
return await this.checkConnection();
|
||||
}
|
||||
|
||||
// Очистка ресурсов
|
||||
destroy() {
|
||||
this.stopReconnect();
|
||||
if (this.checkInterval) {
|
||||
clearInterval(this.checkInterval);
|
||||
this.checkInterval = null;
|
||||
}
|
||||
this.listeners.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Создаём единственный экземпляр сервиса
|
||||
export const connectionService = new ConnectionService();
|
||||
Reference in New Issue
Block a user