const { execFile } = require('child_process'); const http = require('http'); const fs = require('fs'); const path = require('path'); const formidable = require('formidable'); // Ajout de formidable pour analyser les requêtes multipart/form-data // Importer dotenv pour gérer les variables d'environnement require('dotenv').config(); const { Storage } = require('@google-cloud/storage'); // Si la variable GOOGLE_APPLICATION_CREDENTIALS n'est pas définie, on la définit ici. if (!process.env.GOOGLE_APPLICATION_CREDENTIALS) { process.env.GOOGLE_APPLICATION_CREDENTIALS = path.join(__dirname, 'service-key.json'); } // Instancier le client Google Cloud Storage const storage = new Storage(); const bucketName = "test3-2d896.appspot.com"; // Chemin vers le dossier contenant les fichiers statiques const staticDir = path.join(__dirname, 'public'); // Fonction pour servir les fichiers statiques function serveStaticFile(req, res) { let filePath = path.join(staticDir, req.url === '/' ? 'index.html' : req.url); // Normalisez le chemin pour éviter des caractères relatifs comme "../" filePath = path.normalize(filePath); // Vérifiez que le fichier demandé reste dans le dossier `public` if (!filePath.startsWith(staticDir)) { res.writeHead(403, { 'Content-Type': 'text/plain' }); res.end('Accès refusé'); return; } // Vérifiez si le fichier existe fs.stat(filePath, (err, stats) => { if (err || !stats.isFile()) { res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('Fichier non trouvé'); return; } // Détecte le type MIME basé sur l'extension const ext = path.extname(filePath).toLowerCase(); const mimeTypes = { '.html': 'text/html', '.css': 'text/css', '.js': 'application/javascript', '.json': 'application/json', '.png': 'image/png', '.jpg': 'image/jpeg', '.svg': 'image/svg+xml', '.wasm': 'application/wasm', }; const contentType = mimeTypes[ext] || 'application/octet-stream'; // Lit et sert le fichier fs.readFile(filePath, (err, data) => { if (err) { res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Erreur du serveur'); return; } res.writeHead(200, { 'Content-Type': contentType }); res.end(data); }); }); } const server = http.createServer((req, res) => { // Ajouter les en-têtes CORS nécessaires pour toutes les requêtes res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); // Gérer les requêtes OPTIONS (préflight request) if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; } // Servir les fichiers statiques pour les requêtes GET if (req.method === 'GET') { serveStaticFile(req, res); return; } // Endpoint : Création des données JSON pour une visite if (req.method === 'POST' && req.url === '/create-visite-data') { const bucketName = "test3-2d896.appspot.com"; const form = new formidable.IncomingForm(); form.parse(req, async (err, fields) => { if (err) { console.error('Erreur lors de l\'analyse de la requête :', err); res.writeHead(400, { 'Content-Type': 'text/plain' }); res.end('Erreur lors de l\'analyse de la requête.'); return; } try { console.log('Métadonnées brutes reçues :', fields.metadata); // Log des données brutes const metadata = JSON.parse(fields.metadata || '{}'); // Parse en JSON console.log('Métadonnées analysées :', metadata); } catch (parseError) { console.error('Erreur lors du parsing JSON :', parseError); res.writeHead(400, { 'Content-Type': 'text/plain' }); res.end('Erreur dans le format du JSON envoyé.'); } }); return; } if (req.method === 'POST' && req.url === '/upload-to-gcs') { let body = ''; req.on('data', chunk => { body += chunk.toString(); // Convertir les chunks en chaîne }); req.on('end', () => { try { // Analysez le corps de la requête en JSON const { folderPath, jsonData } = JSON.parse(body); // Loguer le chemin du dossier et les données pour voir ce qui est reçu console.log('Chemin du dossier:', folderPath); console.log('Données JSON:', JSON.stringify(jsonData, null, 2)); // Chemin vers le script local_script.js const scriptPath = path.resolve(__dirname, 'local_script.js'); // Utilisez path.resolve pour obtenir un chemin absolu console.log(`Chemin absolu du script : ${scriptPath}`); // Exécuter le script Node.js avec les paramètres appropriés const args = [folderPath, JSON.stringify(jsonData)]; console.log(`Commande exécutée : node ${scriptPath} ${args.join(' ')}`); // Affichez la commande exécutée pour vérification // Augmentez maxBuffer si nécessaire pour éviter les problèmes liés à la taille des logs execFile('node', [scriptPath, ...args], { maxBuffer: 1024 * 1024 * 50 }, (error, stdout, stderr) => { if (error) { console.error(`Erreur lors de l'exécution de local_script.js :`, error); console.error(`stderr:`, stderr); res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Erreur lors du téléversement.'); return; // Sortir pour éviter un double envoi } else { console.log(`Téléversement réussi : ${stdout}`); res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Fichier téléversé avec succès vers Google Cloud Storage.'); } }); } catch (parseError) { console.error('Erreur lors de la lecture du corps de la requête:', parseError); res.writeHead(400, { 'Content-Type': 'text/plain' }); res.end('Erreur de format du corps de la requête.'); } }); return; // Sortir après avoir traité cette route } if (req.method === 'POST' && req.url === '/create-visit') { const form = new formidable.IncomingForm(); form.multiples = true; form.uploadDir = './uploads'; if (!fs.existsSync(form.uploadDir)) { fs.mkdirSync(form.uploadDir, { recursive: true }); } const bucketName = "test3-2d896.appspot.com"; form.parse(req, async (err, fields, files) => { if (err) { console.error('Erreur lors de l\'analyse de la requête :', err); res.writeHead(400, { 'Content-Type': 'text/plain' }); res.end('Erreur lors de l\'analyse de la requête.'); return; } try { const metadata = JSON.parse(fields.metadata || '{}'); const { jsonData } = metadata; console.log('Données JSON reçues :', jsonData); const folderName = jsonData.folderName; if (!folderName) { throw new Error("Nom de dossier (folderName) manquant ou incorrect."); } console.log('Nom du dossier pour les fichiers téléversés :', folderName); const uploadedFiles = Array.isArray(files.files) ? files.files : [files.files]; const uploadedUrls = []; for (const file of uploadedFiles) { const destinationPath = `${folderName}/${file.originalFilename}`; console.log(`Téléversement du fichier : ${destinationPath}`); await storage.bucket(bucketName).upload(file.filepath, { destination: destinationPath, metadata: { contentType: file.mimetype, }, }); uploadedUrls.push(`https://storage.googleapis.com/${bucketName}/${destinationPath}`); } jsonData.mediaUrls = uploadedUrls; const jsonFilePath = `${folderName}/visit_data.json`; const jsonBuffer = Buffer.from(JSON.stringify(jsonData, null, 2)); console.log(`Téléversement du fichier JSON : ${jsonFilePath}`); await storage.bucket(bucketName).file(jsonFilePath).save(jsonBuffer, { contentType: 'application/json', }); console.log('Fichiers et données téléversés avec succès.'); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: true, message: 'Données de la visite téléversées avec succès.', jsonFilePath, })); } catch (error) { console.error('Erreur lors du traitement des données :', error); res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Erreur lors du traitement des données.'); } }); return; } // Route pour générer une URL signée if (req.method === 'POST' && req.url === '/generate-signed-url') { let body = ''; req.on('data', chunk => { body += chunk.toString(); }); req.on('end', async () => { try { // Extraire le `dirUrl` du corps de la requête const { dirUrl } = JSON.parse(body); if (!dirUrl) { res.writeHead(400, { 'Content-Type': 'text/plain' }); res.end('dirUrl est manquant'); return; } // Nettoyer les segments relatifs du chemin avant de générer l'URL signée const cleanedDirUrl = dirUrl.replace(/\/?\.\.\//g, ''); const bucketName = 'test3-2d896.appspot.com'; const filePath = `users/visite_3D/${cleanedDirUrl}`; // Utiliser le chemin nettoyé sans encodage const options = { version: 'v4', action: 'read', expires: Date.now() + 15 * 60 * 1000, // URL valable pour 15 minutes }; // Générer une URL signée (GCS s'occupe de l'encodage) const [url] = await storage.bucket(bucketName).file(filePath).getSignedUrl(options); console.log(`URL signée générée : ${url}`); // Répondre avec l'URL signée res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ url })); } catch (error) { console.error('Erreur lors de la génération de l\'URL signée:', error); res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Erreur lors de la génération de l\'URL signée.'); } }); return; } // Nouvelle route pour obtenir le GCS token if (req.method === 'GET' && req.url === '/get-gcs-token') { try { // Générez ou récupérez le token GCS ici const gcsToken = '9be5e912-95e8-4e13-841e-8be3bf99505d'; // Remplacez cela par la méthode de génération dynamique si nécessaire // Répondre avec le token res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ token: gcsToken })); } catch (error) { console.error('Erreur lors de la récupération du jeton GCS:', error); res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Erreur lors de la récupération du jeton GCS.'); } return; } // Si aucune des routes n'est trouvée, envoyer une réponse 404 res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('Route non trouvée'); }); server.listen(7860, () => { console.log('Serveur en écoute sur le port 7860'); });