- Добавлена полная интеграция с 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 интеграции
133 lines
4.9 KiB
JavaScript
133 lines
4.9 KiB
JavaScript
// ============================================================================
|
||
// n8n Code Node: HTML → PDF через браузер (Puppeteer/Playwright)
|
||
// ============================================================================
|
||
// Используйте этот код ПОСЛЕ ноды, которая вернула HTML или html_base64
|
||
// Подготавливает команду для Execute Command ноды с puppeteer
|
||
// ============================================================================
|
||
|
||
// Получаем HTML из предыдущей ноды
|
||
let html = null;
|
||
|
||
// Вариант 1: HTML уже есть в json.html
|
||
if ($json.html) {
|
||
html = $json.html;
|
||
}
|
||
// Вариант 2: HTML в base64
|
||
else if ($json.html_base64) {
|
||
html = Buffer.from($json.html_base64, 'base64').toString('utf8');
|
||
}
|
||
// Вариант 3: HTML в другом поле
|
||
else if ($json.body?.html) {
|
||
html = $json.body.html;
|
||
}
|
||
// Вариант 4: Пытаемся получить из binary
|
||
else if ($binary && $binary.data) {
|
||
html = $binary.data.toString('utf8');
|
||
}
|
||
else {
|
||
throw new Error('HTML не найден. Проверьте, что предыдущая нода вернула html или html_base64');
|
||
}
|
||
|
||
console.log('📄 HTML получен, длина:', html.length);
|
||
|
||
// ================== ВАРИАНТ 1: Execute Command с Puppeteer ==================
|
||
// Требует: npm install puppeteer в контейнере n8n
|
||
// Команда для Execute Command ноды:
|
||
|
||
const htmlBase64 = Buffer.from(html, 'utf8').toString('base64');
|
||
const timestamp = Date.now();
|
||
const htmlFile = `/tmp/flights-${timestamp}.html`;
|
||
const pdfFile = `/tmp/flights-${timestamp}.pdf`;
|
||
|
||
// Команда для Execute Command ноды:
|
||
const command = `node -e "
|
||
const puppeteer = require('puppeteer');
|
||
const fs = require('fs');
|
||
const html = Buffer.from('${htmlBase64}', 'base64').toString('utf8');
|
||
(async () => {
|
||
const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] });
|
||
const page = await browser.newPage();
|
||
await page.setContent(html, { waitUntil: 'networkidle0' });
|
||
await page.pdf({
|
||
path: '${pdfFile}',
|
||
format: 'A4',
|
||
printBackground: true,
|
||
margin: { top: '20mm', right: '15mm', bottom: '20mm', left: '15mm' }
|
||
});
|
||
await browser.close();
|
||
const pdfBuffer = fs.readFileSync('${pdfFile}');
|
||
const base64 = pdfBuffer.toString('base64');
|
||
console.log(base64);
|
||
fs.unlinkSync('${pdfFile}');
|
||
})();
|
||
"`;
|
||
|
||
return [{
|
||
json: {
|
||
// Команда для Execute Command ноды
|
||
command: command,
|
||
|
||
// Или используйте этот вариант (проще):
|
||
html_file: htmlFile,
|
||
pdf_file: pdfFile,
|
||
html_base64: htmlBase64,
|
||
|
||
// Инструкция
|
||
instruction: 'Используйте Execute Command ноду с одной из команд ниже'
|
||
}
|
||
}];
|
||
|
||
// ================== ВАРИАНТ 2: HTTP Request к сервису с браузером ==================
|
||
// Раскомментируйте, если используете внешний сервис (Gotenberg, Browserless, etc.)
|
||
|
||
/*
|
||
const PDF_SERVICE_URL = 'https://api.gotenberg.dev/forms/chromium/convert/html';
|
||
// Или Browserless: 'https://chrome.browserless.io/pdf'
|
||
|
||
return [{
|
||
json: {
|
||
method: 'POST',
|
||
url: PDF_SERVICE_URL,
|
||
headers: {
|
||
'Content-Type': 'multipart/form-data'
|
||
},
|
||
body: {
|
||
files: [{
|
||
name: 'index.html',
|
||
content: html
|
||
}],
|
||
options: {
|
||
format: 'A4',
|
||
printBackground: true,
|
||
margin: {
|
||
top: '20mm',
|
||
right: '15mm',
|
||
bottom: '20mm',
|
||
left: '15mm'
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}];
|
||
*/
|
||
|
||
// ============================================================================
|
||
// ИНСТРУКЦИЯ ПО ИСПОЛЬЗОВАНИЮ:
|
||
// ============================================================================
|
||
// ВАРИАНТ 1: Execute Command (если puppeteer установлен)
|
||
// 1. Установите puppeteer в контейнере n8n:
|
||
// docker exec -it <n8n_container> npm install puppeteer
|
||
// 2. Добавьте Execute Command ноду после этого Code Node
|
||
// 3. В команде используйте: {{ $json.command }}
|
||
// 4. После Execute Command добавьте Code Node для извлечения base64 из вывода
|
||
//
|
||
// ВАРИАНТ 2: HTTP Request к Gotenberg (self-hosted браузер)
|
||
// 1. Запустите Gotenberg: docker run -p 3000:3000 gotenberg/gotenberg:7
|
||
// 2. Используйте код выше (раскомментируйте)
|
||
// 3. Добавьте HTTP Request ноду
|
||
//
|
||
// ВАРИАНТ 3: HTTP Request к Browserless (cloud сервис)
|
||
// 1. Зарегистрируйтесь на browserless.io
|
||
// 2. Используйте их API для конвертации
|
||
// ============================================================================
|