103 lines
6.7 KiB
JavaScript
103 lines
6.7 KiB
JavaScript
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 = `<!DOCTYPE html><html><head><meta charset="UTF-8" /><link rel="stylesheet" href="https://sheep-service.com/lib/components/leaflet/leaflet.css" /><script src="https://sheep-service.com/lib/components/leaflet/leaflet.js"></script><script src="https://sheep-service.com/lib/components/qrcode.min.js"></script><style>@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap");html, body {height: calc(100% + 50px);}* {border: 0;padding: 0;font-family: "Roboto", sans-serif;margin: 0;font-weight: 500;outline: none;}#content {width: 100%;height: 100%;position: absolute;z-index: 9999;display: flex;flex-direction: column;}#content > #title {width: calc(100% - 30px);min-height: 90px;background: rgb(255 255 255 / 51%);backdrop-filter: blur(10px);display: flex;flex-direction: row;justify-content: space-between;padding: 15px;align-items: center;color: #333;}#content > #title > div {display: flex;flex-direction: column;width: 100%;align-items: center;}#content > #title > h1 {font-size: 60px;font-weight: 700;margin: 0;min-width: 200px;text-align: center;}#content > #title > div > h2 {font-size: 35px;font-weight: 700;margin: 0;}#content > #title > div > h3 {font-size: 28px;font-weight: 500;margin: 0px 0 10px 0;}#qrcode {position: absolute;bottom: 20px;left: 20px;width: 100px;height: 100px;background: #fff;padding: 10px;border-radius: 2px;}.line-left {position: absolute;left: 0;top: 120px;width: 10px;height: calc(100% - 120px);background: rgb(255 255 255 / 51%);backdrop-filter: blur(10px);}.line-right {position: absolute;right: 0;top: 120px;width: 10px;height: calc(100% - 120px);background: rgb(255 255 255 / 51%);backdrop-filter: blur(10px);}.line-bottom {position: absolute;left: 10px;bottom: 0;width: calc(100% - 20px);height: 10px;background: rgb(255 255 255 / 51%);backdrop-filter: blur(10px);}#map {width: 100%;height: 100%;}</style></head><body><div id="content"><div id="title"><div><h3>Картка плану території</h3><h2 id="address"></h2></div><h1 id="number"></h1></div><div id="qrcode"></div><div class="line-left"></div><div class="line-right"></div><div class="line-bottom"></div></div><div id="map"></div><script>const urlParams = new URLSearchParams(window.location.search);let coordinates = ${JSON.stringify(data.points)};let center = [${data.geo.lat}, ${data.geo.lng}];let zoom = ${data.zoom};let type = "${type}";let address = "${data.title} ${data.number ?? ""}";let osm = L.tileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",{});let mytile = L.tileLayer("https://sheep-service.com/map/{z}/{x}/{y}.webp",{maxZoom: 20,minZoom: 15,tms: true,});let map = L.map("map", {renderer: L.canvas(),center,zoom,zoomControl: false,layers: [osm, mytile],});let baseMaps = {OpenStreetMap: osm,"Sheep Service Map": mytile,};let polygonOptions =type === "homestead"? {color: "#f2bd53",radius: 500,fillOpacity: 0.3,dashArray: "20,15",dashOffset: "20",}: {color: "#585858",fillColor: "#f2bd53",fillOpacity: 0.8,};window.onload = async function () {let polygon = L.polygon(coordinates, polygonOptions).addTo(map);let text = 'https://www.google.com/maps/search/?api=1&query='+center[0]+','+center[1];new QRCode(document.getElementById("qrcode"), {text: text,width: 100,height: 100,correctLevel: QRCode.CorrectLevel.L,version: 5,});document.getElementById("address").innerText = address;document.getElementById("number").innerText = '${name}';};</script></body></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; |