feat: Telegram Mini App integration and UX improvements

- Добавлена полная интеграция с Telegram Mini App (динамическая загрузка SDK)
- Отдельный компактный дизайн для Telegram Mini App
- Добавлен loader при инициализации (предотвращает мелькание SMS-авторизации)
- Улучшена навигация: кнопки "Назад" и "К списку заявок" теперь сохраняют авторизацию
- Telegram Mini App: кнопка "Выход" просто закрывает приложение
- Telegram Mini App: заявки "В работе" скрыты из списка
- Веб-версия: для заявок "В работе" добавлена кнопка "Просмотреть в Telegram" (ссылка на @klientprav_bot)
- Telegram Mini App: кнопки действий в черновиках расположены вертикально
- Веб-версия: убрано отображение номера телефона в приветствии
- Исправлена проблема с возвратом к списку черновиков (не требует повторной SMS-авторизации)
- Заблокировано удаление и редактирование заявок со статусом "В работе"
- Добавлена документация по Telegram Mini App интеграции
This commit is contained in:
AI Assistant
2026-01-29 16:12:48 +03:00
parent 73524465fd
commit 2e45786e46
57 changed files with 6776 additions and 234 deletions

View File

@@ -0,0 +1,320 @@
# Пример Workflow для обработки рейсов с Base64 PDF
## Структура Workflow
```
HTTP Request (FlightAware)
HTTP Request (FlightRadar24)
Code: Process Flights Data ← N8N_CODE_PROCESS_FLIGHTS_DATA.js
Code: Prepare PDF Request ← N8N_FLIGHTS_PDF_BASE64_COMPLETE.js
HTTP Request (Convert to PDF) ← Внешний сервис конвертации
Code: Extract Base64 PDF ← N8N_FLIGHTS_PDF_BASE64_FULL.js
[Использование base64 PDF]
├─→ Save File
├─→ Send Email
├─→ Upload to S3
└─→ Return in API Response
```
## Детальная настройка нод
### 1. HTTP Request: FlightAware
- **Method:** GET/POST (в зависимости от API)
- **URL:** `https://flightaware.com/api/...`
- **Authentication:** По необходимости
### 2. HTTP Request: FlightRadar24
- **Method:** GET/POST (в зависимости от API)
- **URL:** `https://flightradar24.com/api/...`
- **Authentication:** По необходимости
### 3. Code: Process Flights Data
**Код:** Скопируйте из `N8N_CODE_PROCESS_FLIGHTS_DATA.js`
**Входные данные:**
- Два элемента из предыдущих HTTP Request нод
**Выходные данные:**
```json
{
"html": "<!DOCTYPE html>...",
"flights": [...],
"flights_count": 2,
"sources": {...},
"generated_at": "2026-01-14T..."
}
```
### 4. Code: Prepare PDF Request
**Код:** Скопируйте из `N8N_FLIGHTS_PDF_BASE64_COMPLETE.js`
**Настройка:**
- Замените `PDF_SERVICE_URL` на URL вашего сервиса
- Замените `PDF_API_KEY` на ваш API ключ
**Выходные данные:**
```json
{
"http_method": "POST",
"http_url": "https://api.htmlpdfapi.com/v1/pdf",
"http_headers": {...},
"http_body": "{...}",
"html_length": 12345,
"flights_count": 2
}
```
### 5. HTTP Request: Convert to PDF
**Настройка:**
- **Method:** `{{ $json.http_method }}`
- **URL:** `{{ $json.http_url }}`
- **Authentication:** По необходимости (через Headers)
- **Headers:**
```json
{{ $json.http_headers }}
```
- **Body:**
```json
{{ $json.http_body }}
```
- **Response Format:** `JSON` или `Binary` (зависит от сервиса)
**Пример для htmlpdfapi.com:**
```json
{
"method": "POST",
"url": "https://api.htmlpdfapi.com/v1/pdf",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_KEY"
},
"body": {
"html": "{{ $('Code: Process Flights Data').first().json.html }}",
"options": {
"format": "A4",
"printBackground": true
},
"base64": true
}
}
```
### 6. Code: Extract Base64 PDF
**Код:** Скопируйте из `N8N_FLIGHTS_PDF_BASE64_FULL.js`
**Входные данные:**
- Ответ от HTTP Request ноды (JSON или Binary)
**Выходные данные:**
```json
{
"pdf_base64": "JVBERi0xLjQKJeLjz9MKMyAwIG9iago8PC9MZW5ndGg...",
"pdf_size_bytes": 123456,
"pdf_size_mb": "0.12",
"success": true,
"source": "json_response"
}
```
## Использование base64 PDF
### Вариант A: Сохранение в файл
**Code Node:**
```javascript
const base64 = $('Code: Extract Base64 PDF').first().json.pdf_base64;
const filename = `flights-report-${new Date().toISOString().split('T')[0]}.pdf`;
// Конвертируем base64 в binary
const pdfBuffer = Buffer.from(base64, 'base64');
return [{
binary: {
data: pdfBuffer,
fileName: filename,
mimeType: 'application/pdf'
},
json: {
filename: filename,
size_bytes: pdfBuffer.length
}
}];
```
Затем используйте ноду **Write Binary File** или **Save to S3**.
### Вариант B: Отправка по Email
**Code Node:**
```javascript
const base64 = $('Code: Extract Base64 PDF').first().json.pdf_base64;
const pdfBuffer = Buffer.from(base64, 'base64');
return [{
json: {
to: 'recipient@example.com',
subject: 'Отчёт о рейсах',
text: 'Во вложении отчёт о рейсах.',
attachments: [{
filename: 'flights-report.pdf',
content: pdfBuffer,
contentType: 'application/pdf'
}]
}
}];
```
Затем используйте ноду **Email Send**.
### Вариант C: Возврат в API Response
**Code Node (перед Response нодой):**
```javascript
const base64 = $('Code: Extract Base64 PDF').first().json.pdf_base64;
const processedData = $('Code: Process Flights Data').first().json;
return [{
json: {
success: true,
flights_count: processedData.flights_count,
pdf_base64: base64,
pdf_size_mb: $('Code: Extract Base64 PDF').first().json.pdf_size_mb,
generated_at: processedData.generated_at
}
}];
```
### Вариант D: Загрузка в S3/Nextcloud
**Code Node:**
```javascript
const base64 = $('Code: Extract Base64 PDF').first().json.pdf_base64;
const pdfBuffer = Buffer.from(base64, 'base64');
const filename = `flights-report-${new Date().toISOString().split('T')[0]}.pdf`;
return [{
binary: {
data: pdfBuffer,
fileName: filename,
mimeType: 'application/pdf'
},
json: {
bucket: 'your-bucket',
key: `reports/${filename}`,
contentType: 'application/pdf'
}
}];
```
Затем используйте ноду **S3 Upload** или **Nextcloud Upload**.
## Альтернативные сервисы конвертации
### 1. htmlpdfapi.com
```javascript
{
"url": "https://api.htmlpdfapi.com/v1/pdf",
"method": "POST",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_KEY"
},
"body": {
"html": "{{ HTML }}",
"base64": true
}
}
```
### 2. pdfshift.io
```javascript
{
"url": "https://api.pdfshift.io/v3/convert/pdf",
"method": "POST",
"headers": {
"Content-Type": "application/json",
"Authorization": "Basic " + Buffer.from("api:YOUR_API_KEY").toString("base64")
},
"body": {
"source": "{{ HTML }}",
"format": "A4"
}
}
// Ответ содержит base64 в поле "pdf"
```
### 3. api2pdf.com
```javascript
{
"url": "https://v2.api2pdf.com/chrome/html",
"method": "POST",
"headers": {
"Authorization": "YOUR_API_KEY",
"Content-Type": "application/json"
},
"body": {
"html": "{{ HTML }}",
"inlinePdf": true,
"fileName": "flights-report.pdf"
}
}
// Ответ содержит base64 в поле "pdf"
```
### 4. Self-hosted: Gotenberg
```javascript
{
"url": "http://your-gotenberg-server:3000/forms/chromium/convert/html",
"method": "POST",
"headers": {
"Content-Type": "multipart/form-data"
},
"body": {
"files": [{
"name": "index.html",
"content": "{{ HTML }}"
}]
}
}
// Ответ - binary PDF, конвертируем в base64
```
## Отладка
### Проверка HTML
```javascript
const html = $('Code: Process Flights Data').first().json.html;
console.log('HTML length:', html.length);
console.log('HTML preview:', html.substring(0, 500));
```
### Проверка base64
```javascript
const base64 = $('Code: Extract Base64 PDF').first().json.pdf_base64;
console.log('Base64 length:', base64.length);
console.log('Base64 preview:', base64.substring(0, 100));
```
### Проверка размера PDF
```javascript
const data = $('Code: Extract Base64 PDF').first().json;
console.log('PDF size:', data.pdf_size_mb, 'MB');
console.log('PDF size bytes:', data.pdf_size_bytes);
```
## Обработка ошибок
Добавьте IF Node после HTTP Request для проверки успешности:
```javascript
// IF Node: Check PDF Conversion Success
{{ $json.success === true }}
```
Если ошибка - отправьте уведомление или сохраните HTML для ручной конвертации.