const os = require('os'); const fs = require('fs'); const path = require('path'); const { chromium } = require('playwright'); const sharp = require('sharp'); let globalBrowser; const queue = []; let isProcessing = false; // Ініціалізація браузера (один раз на весь життєвий цикл додатка) async function getBrowser() { if (!globalBrowser) { globalBrowser = await chromium.launch({ executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || undefined, args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu', '--disable-dev-shm-usage' ] }); } return globalBrowser; } // Функція обробки черги async function processQueue() { if (isProcessing || queue.length === 0) return; isProcessing = true; const { task, resolve, reject } = queue.shift(); try { const result = await runGeneration(task); resolve(result); } catch (err) { reject(err); } finally { isProcessing = false; processQueue(); // беремо наступне завдання } } // Основна логіка генерації async function runGeneration({ id, type }) { const { DOMAIN, ADMIN_TOKEN, CARDS_PATH } = process.env; const name = type === 'homestead' ? `H${id}` : `T${id}`; const URL = `https://${DOMAIN}/api/${type}/${id}`; const baseDir = path.resolve(CARDS_PATH); const cacheDir = path.join(baseDir, 'cache', type); const outputDir = path.join(baseDir, type); fs.mkdirSync(cacheDir, { recursive: true }); fs.mkdirSync(outputDir, { recursive: true }); const SCREENSHOT_FILE = path.join(cacheDir, `${name}.png`); const OUTPUT_FILE = path.join(outputDir, `${name}.webp`); // 1. Отримання даних const res = await fetch(URL, { headers: { 'Authorization': ADMIN_TOKEN, 'Accept': 'application/json' } }); if (!res.ok) throw new Error(`API Error: ${res.status}`); const data = await res.json(); // 2. Підготовка HTML const tmpFile = path.join(os.tmpdir(), `map_${type}_${id}_${Date.now()}.html`); const html = `

Картка плану території

`; fs.writeFileSync(tmpFile, html, 'utf-8'); let page; try { const browser = await getBrowser(); page = await browser.newPage({ viewport: { width: 811, height: 531 } }); await page.goto(`file://${tmpFile}`, { waitUntil: 'networkidle', timeout: 30000 }); await page.waitForTimeout(2000); const buffer = await page.screenshot(); // Зберігаємо файли await fs.promises.writeFile(SCREENSHOT_FILE, buffer); await sharp(buffer).webp({ quality: 85 }).toFile(OUTPUT_FILE); console.log(`✅ Зображення оновлено: ${name}.webp`); return true; } finally { if (page) await page.close(); if (fs.existsSync(tmpFile)) fs.unlinkSync(tmpFile); // Обов'язково видаляємо сміття } } // Експортуємо функцію, яка додає завдання в чергу function genCards(params) { return new Promise((resolve, reject) => { queue.push({ task: params, resolve, reject }); processQueue(); }); } module.exports = genCards;