# Guide d'implémentation - Service Web2Print Guide étape par étape pour déployer le service de conversion HTML vers PDF sur le VPS Infomaniak. ## Prérequis - Accès SSH au VPS - Accès à l'interface Infomaniak - Domaine `web2print.studio-variable.com` pointant vers le VPS --- ## Phase 1 : Préparation du VPS ### 1.1 Connexion et vérification ```bash # Vérifier Node.js et npm node --version npm --version # Vérifier Apache et PHP apache2 -v php -v ``` ### 1.2 Installation de Node.js (si nécessaire) ```bash # Si Node.js n'est pas installé ou version < 14 sudo apt update sudo apt install curl -y # Installer Node.js via NodeSource (version LTS recommandée) curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - sudo apt install -y nodejs # Vérifier l'installation node --version npm --version ``` ### 1.3 Installation de Paged.js CLI ```bash # Installer pagedjs-cli globalement sudo npm install -g pagedjs-cli # Vérifier l'installation (pagedjs-cli n'a pas d'option --version) npm list -g pagedjs-cli which pagedjs-cli # Vérifier le chemin d'installation (noter pour la config PHP) which pagedjs-cli ``` ### 1.4 Tester Paged.js CLI ```bash # Créer un fichier HTML de test cat > /tmp/test.html << 'EOF'

Test Paged.js CLI

Ceci est un test de génération PDF avec Paged.js.

EOF # Générer un PDF de test pagedjs-cli /tmp/test.html -o /tmp/test.pdf # Vérifier que le PDF est créé ls -lh /tmp/test.pdf file /tmp/test.pdf ``` --- ## Phase 2 : Structure du projet ### 2.1 Créer la structure des dossiers ```bash # Créer le dossier du projet sudo mkdir -p /var/www/web2print cd /var/www/web2print # Créer la structure sudo mkdir -p {public,config,logs,tmp} sudo mkdir -p src/{Controllers,Services,Middleware} # Structure finale : # /var/www/web2print/ # ├── public/ # │ └── index.php # ├── src/ # │ ├── Controllers/ # │ ├── Services/ # │ └── Middleware/ # ├── config/ # │ └── config.php # ├── logs/ # └── tmp/ ``` ### 2.2 Définir les permissions ```bash # Propriétaire : www-data (utilisateur Apache) sudo chown -R www-data:www-data /var/www/web2print # Permissions sudo chmod -R 755 /var/www/web2print sudo chmod -R 775 /var/www/web2print/logs sudo chmod -R 775 /var/www/web2print/tmp ``` --- ## Phase 3 : Configuration Apache ### 3.1 Configurer le VirtualHost ```bash # Créer le fichier de configuration sudo nano /etc/apache2/sites-available/web2print.conf ``` Contenu du fichier `web2print.conf` : ```apache ServerName web2print.studio-variable.com DocumentRoot /var/www/web2print/public Options -Indexes +FollowSymLinks AllowOverride All Require all granted # Logs ErrorLog /var/www/web2print/logs/apache-error.log CustomLog /var/www/web2print/logs/apache-access.log combined # Sécurité Require all denied ``` ### 3.2 Activer le site et modules nécessaires ```bash # Activer le module rewrite sudo a2enmod rewrite # Activer le site sudo a2ensite web2print.conf # Vérifier la configuration sudo apache2ctl configtest # Redémarrer Apache sudo systemctl restart apache2 ``` ### 3.3 Configuration SSL avec Let's Encrypt (recommandé) ```bash # Installer Certbot sudo apt install certbot python3-certbot-apache -y # Obtenir et installer le certificat sudo certbot --apache -d web2print.studio-variable.com # Le renouvellement automatique est configuré par défaut ``` --- ## Phase 4 : Configuration DNS (Interface Infomaniak) ### 4.1 Dans l'interface Infomaniak 1. Aller dans la gestion des domaines 2. Sélectionner le domaine `studio-variable.com` 3. Accéder aux zones DNS 4. Ajouter un enregistrement A : - **Nom** : `web2print` - **Type** : `A` - **Valeur** : `[IP du VPS]` - **TTL** : `3600` ### 4.2 Vérifier la propagation ```bash # Attendre quelques minutes puis tester dig web2print.studio-variable.com curl -I http://web2print.studio-variable.com ``` --- ## Phase 5 : Implémentation du code PHP ### 5.1 Fichier de configuration ```bash sudo nano /var/www/web2print/config/config.php ``` Contenu : ```php [ 'votre-cle-api-secrete-generee-aleatoirement', // Ajouter d'autres clés si nécessaire ], // Chemins 'tmp_dir' => '/var/www/web2print/tmp', 'log_file' => '/var/www/web2print/logs/app.log', // Paged.js CLI 'pagedjs_bin' => '/usr/local/bin/pagedjs-cli', // Vérifier avec: which pagedjs-cli 'pagedjs_timeout' => 60, // secondes // Limites 'max_html_size' => 5 * 1024 * 1024, // 5 MB 'max_execution_time' => 90, // Options PDF par défaut 'pdf_defaults' => [ 'format' => 'A4', 'margin' => '2cm', ], ]; ``` ### 5.2 Middleware d'authentification ```bash sudo nano /var/www/web2print/src/Middleware/AuthMiddleware.php ``` Contenu : ```php config = $config; } public function authenticate(): bool { $apiKey = $_SERVER['HTTP_X_API_KEY'] ?? ''; if (empty($apiKey)) { $this->sendError(401, 'API key missing'); return false; } if (!in_array($apiKey, $this->config['api_keys'], true)) { $this->sendError(403, 'Invalid API key'); return false; } return true; } private function sendError(int $code, string $message): void { http_response_code($code); header('Content-Type: application/json'); echo json_encode(['error' => $message]); exit; } } ``` ### 5.3 Service de génération PDF ```bash sudo nano /var/www/web2print/src/Services/PdfGenerator.php ``` Contenu : ```php config = $config; } public function generate(string $html, ?string $css = null, array $options = []): string { // Créer un fichier HTML temporaire $htmlFile = $this->createTempFile($html, $css); $pdfFile = tempnam($this->config['tmp_dir'], 'pdf_') . '.pdf'; try { // Construire la commande Paged.js CLI $command = $this->buildCommand($htmlFile, $pdfFile, $options); // Exécuter Paged.js CLI $output = []; $returnCode = 0; exec($command . ' 2>&1', $output, $returnCode); if ($returnCode !== 0) { $this->log('Paged.js CLI error: ' . implode("\n", $output)); throw new \Exception('PDF generation failed: ' . implode("\n", $output)); } // Lire le PDF généré if (!file_exists($pdfFile)) { throw new \Exception('PDF file not created'); } $pdfContent = file_get_contents($pdfFile); return $pdfContent; } finally { // Nettoyer les fichiers temporaires @unlink($htmlFile); @unlink($pdfFile); } } private function createTempFile(string $html, ?string $css): string { $fullHtml = $html; // Injecter le CSS dans le HTML si fourni if ($css) { $styleTag = ""; if (stripos($html, '') !== false) { $fullHtml = str_ireplace('', $styleTag . '', $html); } else { // Si pas de , créer une structure HTML complète $fullHtml = "{$styleTag}{$html}"; } } // S'assurer que le HTML a une structure complète if (stripos($fullHtml, '{$fullHtml}"; } $tempFile = tempnam($this->config['tmp_dir'], 'html_') . '.html'; file_put_contents($tempFile, $fullHtml); return $tempFile; } private function buildCommand(string $htmlFile, string $pdfFile, array $options): string { $cmd = escapeshellcmd($this->config['pagedjs_bin']); $cmd .= ' ' . escapeshellarg($htmlFile); $cmd .= ' -o ' . escapeshellarg($pdfFile); // Options supplémentaires pour Paged.js CLI // Note: Paged.js utilise les règles CSS @page pour le format // Les options sont limitées dans la CLI // Timeout (millisecondes) if (!empty($options['timeout'])) { $cmd .= ' --timeout ' . (int)$options['timeout']; } else { $cmd .= ' --timeout ' . ($this->config['pagedjs_timeout'] * 1000); } return $cmd; } private function log(string $message): void { $timestamp = date('Y-m-d H:i:s'); $logMessage = "[{$timestamp}] {$message}\n"; file_put_contents($this->config['log_file'], $logMessage, FILE_APPEND); } } ``` ### 5.4 Contrôleur principal ```bash sudo nano /var/www/web2print/src/Controllers/GenerateController.php ``` Contenu : ```php generator = $generator; $this->config = $config; } public function handle(): void { // Vérifier la méthode HTTP if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->sendError(405, 'Method not allowed'); return; } // Lire le body JSON $input = file_get_contents('php://input'); if (strlen($input) > $this->config['max_html_size']) { $this->sendError(413, 'Request too large'); return; } $data = json_decode($input, true); if (json_last_error() !== JSON_ERROR_NONE) { $this->sendError(400, 'Invalid JSON'); return; } // Valider les données if (empty($data['html'])) { $this->sendError(400, 'HTML content required'); return; } try { // Générer le PDF $pdf = $this->generator->generate( $data['html'], $data['css'] ?? null, $data['options'] ?? [] ); // Retourner le PDF header('Content-Type: application/pdf'); header('Content-Length: ' . strlen($pdf)); echo $pdf; } catch (\Exception $e) { $this->sendError(500, 'PDF generation error: ' . $e->getMessage()); } } private function sendError(int $code, string $message): void { http_response_code($code); header('Content-Type: application/json'); echo json_encode(['error' => $message]); } } ``` ### 5.5 Point d'entrée (index.php) ```bash sudo nano /var/www/web2print/public/index.php ``` Contenu : ```php authenticate()) { exit; } // Router simple $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); if ($uri === '/generate' || $uri === '/') { $generator = new \Web2Print\Services\PdfGenerator($config); $controller = new \Web2Print\Controllers\GenerateController($generator, $config); $controller->handle(); } else { http_response_code(404); header('Content-Type: application/json'); echo json_encode(['error' => 'Not found']); } ``` ### 5.6 Fichier .htaccess ```bash sudo nano /var/www/web2print/public/.htaccess ``` Contenu : ```apache # Activer le moteur de réécriture RewriteEngine On # Rediriger toutes les requêtes vers index.php RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] # Sécurité Require all denied ``` --- ## Phase 6 : Génération de la clé API ### 6.1 Générer une clé sécurisée ```bash # Sur le VPS openssl rand -hex 32 # Exemple de sortie : a7f8e9d4c2b1a3f5e7d9c4b2a1f8e6d5c3b9a7f4e2d8c6b4a2f9e7d5c3b1a8f6 ``` ### 6.2 Ajouter la clé dans la configuration ```bash sudo nano /var/www/web2print/config/config.php ``` Remplacer `'votre-cle-api-secrete-generee-aleatoirement'` par la clé générée. ### 6.3 Sécuriser le fichier de configuration ```bash sudo chmod 640 /var/www/web2print/config/config.php sudo chown www-data:www-data /var/www/web2print/config/config.php ``` --- ## Phase 7 : Tests ### 7.1 Vérifier le chemin de Paged.js CLI ```bash which pagedjs-cli # Mettre à jour 'pagedjs_bin' dans config.php si nécessaire ``` ### 7.2 Test avec curl - HTML simple ```bash # Créer un fichier de test avec CSS Paged Media cat > /tmp/test-request.json << 'EOF' { "html": "

Test PDF Paged.js

Ceci est un test de génération PDF avec Paged.js CLI.

" } EOF # Tester l'API (remplacer YOUR_API_KEY) curl -X POST https://web2print.studio-variable.com/generate \ -H "Content-Type: application/json" \ -H "X-API-Key: YOUR_API_KEY" \ -d @/tmp/test-request.json \ --output /tmp/result.pdf # Vérifier le PDF généré file /tmp/result.pdf ls -lh /tmp/result.pdf ``` ### 7.2b Test avec CSS séparé ```bash # Test avec HTML et CSS séparés cat > /tmp/test-request-2.json << 'EOF' { "html": "

Test avec CSS séparé

Le CSS est injecté séparément.

", "css": "@page { size: A4; margin: 2cm; } body { font-family: Georgia, serif; color: #444; } h1 { color: #d63031; border-bottom: 2px solid #d63031; padding-bottom: 10px; }" } EOF curl -X POST https://web2print.studio-variable.com/generate \ -H "Content-Type: application/json" \ -H "X-API-Key: YOUR_API_KEY" \ -d @/tmp/test-request-2.json \ --output /tmp/result-2.pdf ``` ### 7.3 Test sans clé API (doit échouer) ```bash curl -X POST https://web2print.studio-variable.com/generate \ -H "Content-Type: application/json" \ -d '{"html": "

Test

"}' # Doit retourner : {"error":"API key missing"} ``` ### 7.4 Test avec mauvaise clé (doit échouer) ```bash curl -X POST https://web2print.studio-variable.com/generate \ -H "Content-Type: application/json" \ -H "X-API-Key: wrong-key" \ -d '{"html": "

Test

"}' # Doit retourner : {"error":"Invalid API key"} ``` --- ## Phase 8 : Monitoring et maintenance ### 8.1 Rotation des logs ```bash sudo nano /etc/logrotate.d/web2print ``` Contenu : ``` /var/www/web2print/logs/*.log { daily rotate 14 compress delaycompress notifempty create 0640 www-data www-data sharedscripts } ``` ### 8.2 Nettoyage des fichiers temporaires ```bash # Créer un script de nettoyage sudo nano /var/www/web2print/cleanup.sh ``` Contenu : ```bash #!/bin/bash # Nettoyer les fichiers temporaires de plus de 1 heure find /var/www/web2print/tmp -type f -mmin +60 -delete ``` ```bash # Rendre exécutable sudo chmod +x /var/www/web2print/cleanup.sh # Ajouter au cron (toutes les heures) sudo crontab -e # Ajouter : 0 * * * * /var/www/web2print/cleanup.sh ``` ### 8.3 Surveiller les logs ```bash # Logs Apache sudo tail -f /var/www/web2print/logs/apache-error.log # Logs applicatifs sudo tail -f /var/www/web2print/logs/app.log ``` --- ## Phase 9 : Checklist de déploiement - [ ] Node.js et npm installés - [ ] Paged.js CLI installé et fonctionnel - [ ] Structure de dossiers créée avec bonnes permissions - [ ] VirtualHost Apache configuré - [ ] SSL/TLS configuré (Let's Encrypt) - [ ] DNS configuré dans Infomaniak - [ ] Tous les fichiers PHP créés - [ ] Chemin `pagedjs_bin` vérifié dans config.php - [ ] Clé API générée et configurée - [ ] Tests d'API réussis (HTML simple + CSS séparé) - [ ] Rotation des logs configurée - [ ] Nettoyage automatique des fichiers temporaires configuré - [ ] Documentation de la clé API sauvegardée en lieu sûr --- ## Phase 10 : Optimisations futures (optionnel) ### 10.1 Rate limiting Ajouter un système de limitation de requêtes par clé API pour éviter les abus. ### 10.2 Cache Mettre en cache les PDFs générés si le même HTML est demandé plusieurs fois. ### 10.3 File d'attente Pour des volumes plus importants, implémenter une file d'attente avec des workers. ### 10.4 Monitoring avancé - Intégrer un système de monitoring (Prometheus, Grafana) - Alertes sur échecs de génération - Métriques de performance --- ## Ressources et documentation - **Paged.js** : https://pagedjs.org/ - **Paged.js CLI** : https://gitlab.coko.foundation/pagedjs/pagedjs-cli - **CSS Paged Media** : https://www.w3.org/TR/css-page-3/ - **PHP exec()** : https://www.php.net/manual/fr/function.exec.php - **Apache VirtualHost** : https://httpd.apache.org/docs/2.4/vhosts/ - **Let's Encrypt** : https://letsencrypt.org/ --- ## Support et troubleshooting ### Erreur : "pagedjs-cli: command not found" ```bash # Vérifier l'installation which pagedjs-cli npm list -g pagedjs-cli # Réinstaller si nécessaire sudo npm install -g pagedjs-cli --force # Si problème de PATH, vérifier où npm installe les binaires npm config get prefix # Les binaires sont dans: /bin ``` ### Erreur : "Permission denied" lors de la génération ```bash # Vérifier les permissions sudo chown -R www-data:www-data /var/www/web2print sudo chmod -R 775 /var/www/web2print/tmp sudo chmod -R 775 /var/www/web2print/logs ``` ### PDF vide ou erreur de génération - Vérifier les logs : `/var/www/web2print/logs/app.log` - Tester Paged.js CLI manuellement : ```bash echo 'Test' > /tmp/test.html pagedjs-cli /tmp/test.html -o /tmp/test.pdf ``` - Vérifier que Node.js est à jour (minimum v14) - S'assurer que le HTML contient `` et une structure complète ### Erreur : "Timeout" lors de génération de gros documents - Augmenter `pagedjs_timeout` dans `config.php` - Augmenter `max_execution_time` dans `config.php` - Augmenter le timeout Apache si nécessaire : ```bash sudo nano /etc/apache2/apache2.conf # Ajouter/modifier : Timeout 300 sudo systemctl restart apache2 ``` ### Les styles CSS ne sont pas appliqués - Vérifier que le CSS contient les règles `@page` pour Paged.js - S'assurer que les ressources externes (fonts, images) sont accessibles - Tester le HTML directement dans un navigateur avec Paged.js - Exemple de CSS Paged Media basique : ```css @page { size: A4; margin: 2cm; } @page :first { margin-top: 3cm; } ``` ### Différences de rendu entre front et back - Utiliser exactement la même version de Paged.js (front) et Paged.js CLI (back) - Vérifier la version installée : `npm list -g pagedjs-cli` - Les fonts doivent être identiques (utiliser web fonts ou fonts système communes)