Исправлено дублирование сообщений в task-чате: защита от двойного Enter/клика по кнопке отправки
This commit is contained in:
@@ -1,95 +0,0 @@
|
||||
/**
|
||||
* Клиент Traccar REST API.
|
||||
* Настройка через env: TRACCAR_API_URL, TRACCAR_USER, TRACCAR_PASSWORD.
|
||||
* Если переменные не заданы — все методы тихо no-op с логом,
|
||||
* чтобы GPS-модуль работал и без Traccar (например, только PWA-трекинг).
|
||||
*/
|
||||
|
||||
const TRACCAR_API_URL = process.env.TRACCAR_API_URL;
|
||||
const TRACCAR_USER = process.env.TRACCAR_USER;
|
||||
const TRACCAR_PASSWORD = process.env.TRACCAR_PASSWORD;
|
||||
|
||||
function isConfigured(): boolean {
|
||||
return Boolean(TRACCAR_API_URL && TRACCAR_USER && TRACCAR_PASSWORD);
|
||||
}
|
||||
|
||||
function authHeaders(): Record<string, string> {
|
||||
const basic = Buffer.from(`${TRACCAR_USER}:${TRACCAR_PASSWORD}`).toString("base64");
|
||||
return {
|
||||
Authorization: `Basic ${basic}`,
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
}
|
||||
|
||||
interface TraccarDevice {
|
||||
id: number;
|
||||
uniqueId: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
/** Найти устройство по uniqueId; возвращает null, если не найдено */
|
||||
async function findDevice(uniqueId: string): Promise<TraccarDevice | null> {
|
||||
const url = `${TRACCAR_API_URL}/api/devices?uniqueId=${encodeURIComponent(uniqueId)}`;
|
||||
const res = await fetch(url, { headers: authHeaders() });
|
||||
if (!res.ok) {
|
||||
throw new Error(`Traccar GET /api/devices: HTTP ${res.status}`);
|
||||
}
|
||||
const devices = (await res.json()) as TraccarDevice[];
|
||||
return devices.find((d) => d.uniqueId === uniqueId) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт устройство в Traccar, если его ещё нет.
|
||||
* Возвращает id устройства или null при ошибке/отсутствии конфигурации.
|
||||
*/
|
||||
export async function ensureDevice(uniqueId: string, name: string): Promise<number | null> {
|
||||
if (!isConfigured()) {
|
||||
console.log("[GPS] Traccar не настроен (TRACCAR_API_URL/USER/PASSWORD), ensureDevice пропущен");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const existing = await findDevice(uniqueId);
|
||||
if (existing) return existing.id;
|
||||
|
||||
const res = await fetch(`${TRACCAR_API_URL}/api/devices`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({ uniqueId, name }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const text = await res.text().catch(() => "");
|
||||
throw new Error(`Traccar POST /api/devices: HTTP ${res.status} ${text}`);
|
||||
}
|
||||
const device = (await res.json()) as TraccarDevice;
|
||||
console.log(`[GPS] Traccar: создано устройство '${name}' (${uniqueId}), id=${device.id}`);
|
||||
return device.id;
|
||||
} catch (err) {
|
||||
console.error("[GPS] Traccar ensureDevice error:", err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Удаляет устройство из Traccar по uniqueId (если существует) */
|
||||
export async function deleteDevice(uniqueId: string): Promise<void> {
|
||||
if (!isConfigured()) {
|
||||
console.log("[GPS] Traccar не настроен, deleteDevice пропущен");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const existing = await findDevice(uniqueId);
|
||||
if (!existing) return;
|
||||
|
||||
const res = await fetch(`${TRACCAR_API_URL}/api/devices/${existing.id}`, {
|
||||
method: "DELETE",
|
||||
headers: authHeaders(),
|
||||
});
|
||||
if (!res.ok && res.status !== 404) {
|
||||
throw new Error(`Traccar DELETE /api/devices/${existing.id}: HTTP ${res.status}`);
|
||||
}
|
||||
console.log(`[GPS] Traccar: удалено устройство ${uniqueId}`);
|
||||
} catch (err) {
|
||||
console.error("[GPS] Traccar deleteDevice error:", err);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user