Perspicacious commited on
Commit
9c1ac4f
·
verified ·
1 Parent(s): aa30655

Create pdf-server.js

Browse files
Files changed (1) hide show
  1. pdf-server.js +231 -0
pdf-server.js ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const express = require('express');
2
+ const { chromium } = require('playwright-core');
3
+
4
+ const app = express();
5
+ app.use(express.json({ limit: '50mb' }));
6
+
7
+ // ==================== CONFIGURATION ====================
8
+ const PORT = 3000;
9
+ const CHROMIUM_PATH = '/usr/bin/chromium-browser';
10
+
11
+ // Formats KDP
12
+ const FORMATS = {
13
+ '6x9': { width: '6in', height: '9in' },
14
+ '5x8': { width: '5in', height: '8in' },
15
+ '5.5x8.5': { width: '5.5in', height: '8.5in' },
16
+ '8.5x11': { width: '8.5in', height: '11in' },
17
+ 'A4': { width: '210mm', height: '297mm' },
18
+ 'A5': { width: '148mm', height: '210mm' },
19
+ };
20
+
21
+ // ==================== HEALTH CHECK ====================
22
+ app.get('/health', (req, res) => {
23
+ res.json({ status: 'ok', service: 'pdf-server' });
24
+ });
25
+
26
+ // ==================== GÉNÉRATION PDF ====================
27
+ app.post('/generate-pdf', async (req, res) => {
28
+ const startTime = Date.now();
29
+ let browser = null;
30
+
31
+ try {
32
+ const {
33
+ html,
34
+ format = '6x9',
35
+ margins = { top: '0.75in', bottom: '0.75in', left: '0.5in', right: '0.5in' },
36
+ printBackground = true,
37
+ waitForImages = true,
38
+ waitTimeout = 30000,
39
+ } = req.body;
40
+
41
+ if (!html) {
42
+ return res.status(400).json({ error: 'html requis' });
43
+ }
44
+
45
+ console.log(`📄 Génération PDF (format: ${format})...`);
46
+
47
+ // Lancer le navigateur
48
+ browser = await chromium.launch({
49
+ executablePath: CHROMIUM_PATH,
50
+ headless: true,
51
+ args: [
52
+ '--no-sandbox',
53
+ '--disable-setuid-sandbox',
54
+ '--disable-dev-shm-usage',
55
+ '--disable-gpu',
56
+ '--single-process',
57
+ ],
58
+ });
59
+
60
+ const context = await browser.newContext();
61
+ const page = await context.newPage();
62
+
63
+ // Charger le HTML
64
+ await page.setContent(html, {
65
+ waitUntil: 'networkidle',
66
+ timeout: waitTimeout,
67
+ });
68
+
69
+ // Attendre que les images soient chargées
70
+ if (waitForImages) {
71
+ await page.evaluate(() => {
72
+ return Promise.all(
73
+ Array.from(document.images)
74
+ .filter(img => !img.complete)
75
+ .map(img => new Promise(resolve => {
76
+ img.onload = img.onerror = resolve;
77
+ setTimeout(resolve, 5000);
78
+ }))
79
+ );
80
+ });
81
+ }
82
+
83
+ // Attendre un peu pour le rendu CSS
84
+ await page.waitForTimeout(500);
85
+
86
+ // Configuration du format
87
+ const formatConfig = FORMATS[format] || FORMATS['6x9'];
88
+
89
+ // Générer le PDF
90
+ const pdfBuffer = await page.pdf({
91
+ width: formatConfig.width,
92
+ height: formatConfig.height,
93
+ margin: margins,
94
+ printBackground: printBackground,
95
+ preferCSSPageSize: true,
96
+ });
97
+
98
+ await browser.close();
99
+ browser = null;
100
+
101
+ const duration = Date.now() - startTime;
102
+ console.log(`✅ PDF généré (${(pdfBuffer.length / 1024 / 1024).toFixed(2)} MB, ${duration}ms)`);
103
+
104
+ // Retourner en base64
105
+ res.json({
106
+ success: true,
107
+ pdf: pdfBuffer.toString('base64'),
108
+ size_bytes: pdfBuffer.length,
109
+ size_mb: (pdfBuffer.length / 1024 / 1024).toFixed(2),
110
+ duration_ms: duration,
111
+ });
112
+
113
+ } catch (error) {
114
+ console.error('❌ Erreur:', error.message);
115
+ if (browser) await browser.close();
116
+ res.status(500).json({ error: error.message });
117
+ }
118
+ });
119
+
120
+ // ==================== SCREENSHOT ====================
121
+ app.post('/screenshot', async (req, res) => {
122
+ let browser = null;
123
+
124
+ try {
125
+ const {
126
+ url,
127
+ html,
128
+ fullPage = true,
129
+ width = 1920,
130
+ height = 1080,
131
+ } = req.body;
132
+
133
+ if (!url && !html) {
134
+ return res.status(400).json({ error: 'url ou html requis' });
135
+ }
136
+
137
+ browser = await chromium.launch({
138
+ executablePath: CHROMIUM_PATH,
139
+ headless: true,
140
+ args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],
141
+ });
142
+
143
+ const page = await browser.newPage();
144
+ await page.setViewportSize({ width, height });
145
+
146
+ if (url) {
147
+ await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
148
+ } else {
149
+ await page.setContent(html, { waitUntil: 'networkidle' });
150
+ }
151
+
152
+ const screenshot = await page.screenshot({ fullPage, type: 'png' });
153
+ await browser.close();
154
+
155
+ res.json({
156
+ success: true,
157
+ screenshot: screenshot.toString('base64'),
158
+ size_bytes: screenshot.length,
159
+ });
160
+
161
+ } catch (error) {
162
+ if (browser) await browser.close();
163
+ res.status(500).json({ error: error.message });
164
+ }
165
+ });
166
+
167
+ // ==================== SCRAPING ====================
168
+ app.post('/scrape', async (req, res) => {
169
+ let browser = null;
170
+
171
+ try {
172
+ const {
173
+ url,
174
+ selector,
175
+ waitFor,
176
+ evaluate,
177
+ } = req.body;
178
+
179
+ if (!url) {
180
+ return res.status(400).json({ error: 'url requis' });
181
+ }
182
+
183
+ browser = await chromium.launch({
184
+ executablePath: CHROMIUM_PATH,
185
+ headless: true,
186
+ args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],
187
+ });
188
+
189
+ const page = await browser.newPage();
190
+ await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
191
+
192
+ if (waitFor) {
193
+ await page.waitForSelector(waitFor, { timeout: 10000 });
194
+ }
195
+
196
+ let result = {};
197
+
198
+ // Récupérer le HTML
199
+ result.html = await page.content();
200
+ result.title = await page.title();
201
+ result.url = page.url();
202
+
203
+ // Si un sélecteur est spécifié
204
+ if (selector) {
205
+ result.elements = await page.$$eval(selector, els => els.map(el => ({
206
+ text: el.textContent?.trim(),
207
+ html: el.innerHTML,
208
+ href: el.href || null,
209
+ src: el.src || null,
210
+ })));
211
+ }
212
+
213
+ // Si un script personnalisé est fourni
214
+ if (evaluate) {
215
+ result.custom = await page.evaluate(evaluate);
216
+ }
217
+
218
+ await browser.close();
219
+
220
+ res.json({ success: true, ...result });
221
+
222
+ } catch (error) {
223
+ if (browser) await browser.close();
224
+ res.status(500).json({ error: error.message });
225
+ }
226
+ });
227
+
228
+ // ==================== DÉMARRAGE ====================
229
+ app.listen(PORT, '0.0.0.0', () => {
230
+ console.log(`🚀 PDF Server running on port ${PORT}`);
231
+ });