96 lines
3.4 KiB
TypeScript
96 lines
3.4 KiB
TypeScript
/**
|
||
* Клиент 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);
|
||
}
|
||
}
|