Як я втратив 10 годин свого вихідного дня, розв’язуючи цей кошмар з генерацією PDF (щоб ви не витрачали на це свій час!)

text
pic

Дозвольте представитися та пояснити, як виникла ця проблема.

Я є full-stack веб-розробником, з більшим акцентом на фронтенд. Близько року тому я приєднався до компанії, яка працювала над сайтом для подачі іпотечних заявок для клієнта. До того, як я приєднався, над проектом працював лише один розробник, який написав увесь код для генерації PDF за допомогою Puppeteer і Mustache. (Якщо ви не знаєте, що це, не хвилюйтеся, я поясню далі.) Він залишив компанію через деякий час, і тепер я відповідаю за його підтримку.

Як фронтенд-розробник, я не мав жодного уявлення, що таке Puppeteer і навіщо він нам потрібен.

Наш бекенд був розгорнутий на платформі Railway, яка забезпечує чудовий досвід розгортання Docker-застосунків. (Можна також розгорнути безпосередньо, і вони самі займаються Docker-ізацією, але краще створити Dockerfile самостійно.)

Проблема

Нещодавно виникла проблема на сайті: ми відправляли згенерований PDF з нашого бекенду, коли користувач подавав іпотечну заявку, але листи не доставлялися.

Спочатку я подумав, що це може бути пов’язано з закінченням терміну дії нашого плану з постачальником електронної пошти, Brevo. Однак я протестував це на своїй локальній машині з налаштованою електронною поштою, використовуючи SMTP сервер Gmail. (Якщо хочете дізнатися більше, як це працює, слідкуйте за оновленнями; я напишу докладний блог про те, як налаштувати це для ваших проектів.) На моє полегшення, все працювало бездоганно.

Я був майже впевнений, що проблема на 100% пов’язана з нашим акаунтом у Brevo, тому вирішив насолоджуватися вихідними. Але тоді мій менеджер, який курирує всі акаунти, підтвердив, що план Brevo не закінчився і що проблема була з нашим бекендом.

Я був дуже здивований і не знав, що робити, тому що на моїй локальній машині все працювало добре. Я навіть пробував змінювати SMTP-дані в продакшн-середовищі, але все одно нічого не виходило.

Тоді я подумав, що, можливо, Railway блокує вихідні листи з якихось причин. Я подав запит на їх форум, щоб дізнатися, чи почали вони недавно блокувати вихідний SMTP трафік. Один із їх співробітників відповів, що Railway не блокує вихідний трафік на жодному порту.

Тепер я знову був на самому початку: електронний постачальник працює нормально, з Railway все гаразд, і все ще функціонує добре на моїй локальній машині.

Розслідування

Тож я зробив те, що будь-який професійний розробник зробив би: написав масу console.log в моїй гілці (буквально по одному для кожного рядка, один до ініціалізації змінної і один після, тому що я був дуже роздратований тим, що не так з Railway, коли на моїй локальній машині все працювало). Я все відправив у тестове середовище в Railway.

Після годин перевірки логів і підтвердження всього, я виявив, що проблема була пов’язана з пакетом Puppeteer. Тепер дозвольте пояснити, що таке Puppeteer і чому він був використаний у моєму проекті.

Що таке Puppeteer?

Puppeteer — це JavaScript-бібліотека, яка надає високорівневий API для управління Chrome або Firefox через DevTools Protocol або WebDriver BiDi. Puppeteer працює в безголовому режимі (без видимого інтерфейсу за замовчуванням). (Це прямо з їх офіційного сайту; я також повністю не розумію, як це працює!)

Щоб зрозуміти, навіщо він потрібен, уявіть, що немає простого способу перетворити HTML в PDF. Створити HTML-сторінку з динамічними даними за допомогою Mustache дуже просто (ви просто передаєте базовий шаблон і дані в необхідному форматі, і він створює HTML-сторінку з усіма даними, які ви надали).

Щоб перетворити згенерований HTML у PDF, потрібен Puppeteer. Він запускає браузер всередині вашого контейнера, створює нову вкладку браузера і дозволяє виконувати багато операцій, які ви зазвичай робите з цією вкладкою, наприклад, робити скріншоти або завантажувати сторінку як PDF.
text
Отже, він створює сторінку, використовуючи HTML-дані, які ви надаєте, і генерує для вас PDF, який ви можете зберегти в будь-яку папку за допомогою модуля fs.

Puppeteer пропонує багато інших можливостей, але це в основному те, для чого ми його використовували.

Рішення

Тепер давайте повернемося до того, чому це викликало проблему і як я з’ясував, в чому справа. Перевіряючи логи, я постійно бачив, що все працює нормально, але код завжди зупинявся на логі, який я написав перед запуском Puppeteer.

Після пошуку в Google та моєму улюбленому Stack Overflow я дізнався, що версія Puppeteer, яку ми використовували, мала дві проблеми:

  1. Вона була застарілою.
  2. Вона працювала добре на локальних машинах, але мала деякі дивні проблеми при спробі запустити браузер через неї в Railway.

Після кількох годин досліджень я натрапив на репозиторій на GitHub під назвою Browserless. Він фактично знімає навантаження при запуску браузера через Puppeteer. Puppeteer може підключатися до нього та виконувати безголовну роботу всередині Docker.

На моє щастя, я також знайшов готовий шаблон для Railway, який можна розгорнути лише одним кліком на їхній платформі. Ось посилання на цей шаблон та приклад Puppeteer:

Висновок

У світі веб-розробки (особливо в контексті складного JS) непередбачувані проблеми можуть виникати в будь-який момент, часто призводячи до розчарувань. Мій вікенд, який я витратив на усунення проблеми з генерацією PDF, навчив мене важливих уроків про наполегливість, значення розуміння інструментів, які ми використовуємо, і силу таких ресурсів, як GitHub та Stack Overflow. Використовуючи правильні інструменти та ресурси, я зміг вирішити цю проблему та забезпечити безперебійну роботу нашого застосунку.

Якщо ви стикаєтеся з подібними труднощами, пам’ятайте, що ви не одні. Не соромтеся звертатися за допомогою, будь то через форуми, документацію або колег-розробників.

І нарешті, якщо ви хочете дізнатися, як створювати PDF за допомогою Puppeteer і Mustache, напишіть коментар, і я з радістю поділюсь докладним посібником!

До наступної зустрічі, щасливого кодування!

Перекладено з: How I Lost 10 Hours of My Weekend Solving This PDF Generation Nightmare (So You Don’t Have To!)

Leave a Reply

Your email address will not be published. Required fields are marked *