// ============================================================================ // n8n Code Node: Обработка данных о рейсах → Base64 HTML // ============================================================================ // Вход: [{ data: [{ body: { flights: [...] }}, { body: { data: [...] }}] }] // Выход: base64 HTML // ============================================================================ const inputItems = $input.all(); // ================== FALLBACK ================== if (!inputItems || inputItems.length === 0) { const html = '

Ошибка: данные не получены

'; const htmlBase64 = Buffer.from(html, 'utf8').toString('base64'); return [{ json: { html_base64: htmlBase64, html: html, flights_count: 0, error: 'Нет входных данных' } }]; } // ================== ИЗВЛЕЧЕНИЕ ДАННЫХ ================== let flightAwareData = []; let flightRadar24Data = []; let requestData = null; let flightRadar24Error = null; try { const firstItem = inputItems[0]; if (firstItem && firstItem.json && firstItem.json.data && Array.isArray(firstItem.json.data)) { if (firstItem.json.data[0] && firstItem.json.data[0].body) { if (firstItem.json.data[0].body.flights) { flightAwareData = Array.isArray(firstItem.json.data[0].body.flights) ? firstItem.json.data[0].body.flights : []; } } if (firstItem.json.data[1]) { if (firstItem.json.data[1].error) { flightRadar24Error = firstItem.json.data[1].error; flightRadar24Data = []; } else if (firstItem.json.data[1].body && firstItem.json.data[1].body.data) { flightRadar24Data = Array.isArray(firstItem.json.data[1].body.data) ? firstItem.json.data[1].body.data : []; } } if (firstItem.json.data[2] && firstItem.json.data[2].flight_number) { requestData = { flight_number: firstItem.json.data[2].flight_number, departure_date_local: firstItem.json.data[2].departure_date_local || null, arrival_date_local: firstItem.json.data[2].arrival_date_local || null }; } } } catch (e) { console.log('⚠️ Ошибка извлечения данных:', e.message); } // ================== УТИЛИТЫ ================== const safeStr = v => (v == null ? '' : String(v)); const safeDate = v => { if (!v) return '—'; try { const d = new Date(v); return isNaN(d.getTime()) ? '—' : d.toLocaleString('ru-RU', { timeZone: 'UTC', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); } catch { return '—'; } }; const formatDuration = s => !s ? '—' : `${Math.floor(s / 3600)}ч ${Math.floor((s % 3600) / 60)}м`; const formatDistance = km => !km ? '—' : `${Number(km).toFixed(2)} км`; // ================== MERGE: все рейсы FA + FR24 по registration ================== const flightsMap = new Map(); // Ключ для рейса без registration (уникальный per FA-рейс) const faKey = (f, i) => { const reg = safeStr(f.registration).trim(); if (reg) return reg; return (f.fa_flight_id || `FA-${f.ident || 'X'}-${i}-${f.origin?.code_icao}-${f.destination?.code_icao}`).trim(); }; // Добавляем все рейсы из FlightAware (в т.ч. без registration, отменённые) flightAwareData.forEach((f, i) => { const key = faKey(f, i); const reg = safeStr(f.registration).trim(); if (flightsMap.has(key)) { flightsMap.get(key).fa = f; } else { flightsMap.set(key, { registration: reg || '—', flightNumber: safeStr(f.flight_number), ident: safeStr(f.ident), identIata: safeStr(f.ident_iata), aircraftType: safeStr(f.aircraft_type), fa: f, fr: null }); } }); // Добавляем данные из FlightRadar24 (мерж только по registration) flightRadar24Data.forEach(f => { const reg = safeStr(f.reg).trim(); if (!reg) return; if (flightsMap.has(reg)) { flightsMap.get(reg).fr = f; } else { flightsMap.set(reg, { registration: reg, flightNumber: safeStr(f.flight), ident: safeStr(f.callsign), identIata: safeStr(f.flight), aircraftType: safeStr(f.type), fa: null, fr: f }); } }); // ================== ДОБАВЛЕНИЕ ЗАПРОШЕННЫХ РЕЙСОВ БЕЗ ДАННЫХ ================== const allInputItems = $input.all(); const firstItemForRequest = inputItems[0]; let requestedFlightNumbers = new Set(); let requestFlightNumber = null; let requestDepartureDate = null; let requestArrivalDate = null; if (requestData) { requestFlightNumber = requestData.flight_number; requestDepartureDate = requestData.departure_date_local; requestArrivalDate = requestData.arrival_date_local; if (requestFlightNumber) requestedFlightNumbers.add(String(requestFlightNumber)); } allInputItems.forEach(item => { if (!item?.json) return; if (item.json.flight_number && (item.json.departure_date_local || item.json.arrival_date_local) && !requestFlightNumber) { requestFlightNumber = item.json.flight_number || item.json.ident || item.json.flight; requestDepartureDate = item.json.departure_date_local || null; requestArrivalDate = item.json.arrival_date_local || null; if (requestFlightNumber) requestedFlightNumbers.add(String(requestFlightNumber)); } if (item.json.request_flight_number && !requestFlightNumber) { requestFlightNumber = item.json.request_flight_number; requestDepartureDate = item.json.request_departure_date || null; requestArrivalDate = item.json.request_arrival_date || null; if (requestFlightNumber) requestedFlightNumbers.add(String(requestFlightNumber)); } ['flight_number', 'ident', 'flight'].forEach(k => { if (item.json[k]) requestedFlightNumbers.add(String(item.json[k])); }); }); if (firstItemForRequest?.json) { if (Array.isArray(firstItemForRequest.json.requested_flights)) { firstItemForRequest.json.requested_flights.forEach(flight => { const n = typeof flight === 'string' ? flight : (flight.flight_number || flight.ident || flight); if (n) requestedFlightNumbers.add(n); }); } const one = firstItemForRequest.json.flight_number || firstItemForRequest.json.ident || firstItemForRequest.json.flight; if (one) requestedFlightNumbers.add(one); if (Array.isArray(firstItemForRequest.json.flight_numbers)) { firstItemForRequest.json.flight_numbers.forEach(n => { if (n) requestedFlightNumbers.add(String(n)); }); } } requestedFlightNumbers.forEach(flightNum => { let found = false; flightsMap.forEach((flight) => { if (flight.flightNumber === flightNum || flight.ident === flightNum || flight.identIata === flightNum) found = true; }); if (!found) { flightsMap.set(`REQUESTED-${flightNum}`, { registration: '—', flightNumber: flightNum, ident: flightNum, identIata: flightNum, aircraftType: '—', fa: null, fr: null, isRequested: true }); } }); const flights = Array.from(flightsMap.values()); // ================== HTML: карточка рейса ================== const generateFlightCard = (f) => { const fa = f.fa; const fr = f.fr; if (f.isRequested && !fa && !fr) { const matchesRequest = requestFlightNumber && (String(f.flightNumber) === String(requestFlightNumber) || String(f.ident) === String(requestFlightNumber)); let requestInfo = ''; if (matchesRequest) { if (requestDepartureDate) requestInfo += `
Дата вылета (запрос):${requestDepartureDate}
`; if (requestArrivalDate) requestInfo += `
Дата прилёта (запрос):${requestArrivalDate}
`; } return `

Рейс ${f.flightNumber || f.ident || '—'}

Запрошен
Запрошенный рейс:${f.flightNumber || f.ident || '—'}
${requestInfo}
`; } const isCancelled = !!(fa && fa.cancelled); const headerBadge = isCancelled ? 'Отменён' : `${f.registration || '—'}`; let card = `

Рейс ${f.flightNumber || f.ident || '—'}

${headerBadge}
Тип самолёта:${f.aircraftType || '—'}
Идентификатор:${f.ident || '—'} (${f.identIata || '—'})
`; if (fa) { card += `
FlightAware
${isCancelled ? '
✕ Рейс отменён
' : ''}
Откуда: ${safeStr(fa.origin?.name || fa.origin?.code_iata || '—')} (${safeStr(fa.origin?.code_iata || '—')})
Куда: ${safeStr(fa.destination?.name || fa.destination?.code_iata || '—')} (${safeStr(fa.destination?.code_iata || '—')})
Плановый вылет: ${safeDate(fa.scheduled_out)}
Фактический вылет: ${safeDate(fa.actual_out)}
Взлёт: ${safeDate(fa.actual_off)} ${fa.actual_runway_off ? `(ВПП ${fa.actual_runway_off})` : ''}
Посадка: ${safeDate(fa.actual_on)} ${fa.actual_runway_on ? `(ВПП ${fa.actual_runway_on})` : ''}
Фактический прилёт: ${safeDate(fa.actual_in)}
Статус: ${safeStr(fa.status || '—')}
${fa.departure_delay != null ? `
Задержка вылета: ${fa.departure_delay > 0 ? '+' : ''}${Math.floor(fa.departure_delay / 60)} мин
` : ''} ${fa.arrival_delay != null ? `
Задержка прилёта: ${fa.arrival_delay > 0 ? '+' : ''}${Math.floor(fa.arrival_delay / 60)} мин
` : ''} ${fa.gate_origin ? `
Гейт вылета:${fa.gate_origin}
` : ''} ${fa.gate_destination ? `
Гейт прилёта:${fa.gate_destination}
` : ''} ${fa.baggage_claim ? `
Выдача багажа:${fa.baggage_claim}
` : ''}
`; } if (fr) { card += `
FlightRadar24
Откуда:${safeStr(fr.orig_iata || '—')} (${safeStr(fr.orig_icao || '—')})
Куда:${safeStr(fr.dest_iata || '—')} (${safeStr(fr.dest_icao || '—')})
Взлёт: ${safeDate(fr.datetime_takeoff)} ${fr.runway_takeoff ? `(ВПП ${fr.runway_takeoff})` : ''}
Посадка: ${safeDate(fr.datetime_landed)} ${fr.runway_landed ? `(ВПП ${fr.runway_landed})` : ''}
Время полёта:${formatDuration(fr.flight_time)}
Фактическое расстояние:${formatDistance(fr.actual_distance)}
Кратчайшее расстояние:${formatDistance(fr.circle_distance)}
Статус полёта:${fr.flight_ended ? 'Завершён' : 'В процессе'}
`; } card += `
`; return card; }; // ================== HTML ================== const now = new Date(); const reportDate = now.toLocaleString('ru-RU', { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' }); const html = ` Отчёт о рейсах

Отчёт о рейсах

Дата формирования: ${reportDate}
FlightAware: ${flightAwareData.length > 0 ? '✓ Данные получены' : '✗ Данные отсутствуют'} FlightRadar24: ${flightRadar24Data.length > 0 ? '✓ Данные получены' : '✗ Данные отсутствуют'}
${flights.length ? flights.map(f => generateFlightCard(f)).join('') : '
Данные о рейсах не найдены
'}
`; const htmlBase64 = Buffer.from(html, 'utf8').toString('base64'); return [{ json: { html_base64: htmlBase64, html: html, flights_count: flights.length, sources: { flightaware: { available: flightAwareData.length > 0, count: flightAwareData.length }, flightradar24: { available: flightRadar24Data.length > 0, count: flightRadar24Data.length } }, generated_at: now.toISOString() } }];