UNPKG

fleetbo

Version:

Runs and manages Fleetbo projects.

207 lines (162 loc) 7.73 kB
#!/usr/bin/env node const { spawn } = require('child_process'); const fs = require('fs'); const path = require('path'); const axios = require('axios'); const dotenv = require('dotenv'); const ngrok = require('ngrok'); // Module natif (plus de spawn externe) const os = require('os'); // Configuration const UPDATE_NETWORK_URL = 'https://us-central1-myapp-259bf.cloudfunctions.net/updateDeveloperNetwork'; const PORT = 3000; // --- Nettoyage propre à la sortie --- async function cleanupAndExit(code = 0) { console.log('\n[Fleetbo] 🛑 Shutting down services...'); try { await ngrok.kill(); // Coupe le tunnel proprement } catch (e) {} process.exit(code); } process.on('SIGINT', () => cleanupAndExit(0)); process.on('SIGTERM', () => cleanupAndExit(0)); // --- Synchronisation Firebase --- async function syncFirebase(keyApp, networkUrl) { try { await axios.post(UPDATE_NETWORK_URL, { keyApp: keyApp, networkUrl: networkUrl }); console.log('\n[Fleetbo] ---------------------------------------------------'); console.log(`[Fleetbo] ✅ Tunnel Active: ${networkUrl}`); console.log(`[Fleetbo] 🚀 Emulator: https://fleetbo.io/studio/view/${keyApp}`); console.log('[Fleetbo] ---------------------------------------------------\n'); } catch (err) { console.error(`[Fleetbo] ⚠️ Sync Error: Cloud Function unreachable.`); console.error(`[Fleetbo] Detail: ${err.message}`); } } // --- Fonction Principale --- async function runGuardian() { console.log(`[Fleetbo] 🛡️ Starting Fleetbo Guardian on ${os.platform()}...`); const envPath = path.join(process.cwd(), '.env'); if (!fs.existsSync(envPath)) { console.error('Error: .env file not found. Are you in the project root?'); process.exit(1); } dotenv.config({ path: envPath }); const keyApp = process.env.REACT_KEY_APP; if (!keyApp) { console.error('Error: REACT_KEY_APP is missing in .env'); process.exit(1); } // 1. Lancement du Serveur React console.log(`[Fleetbo] 📦 Booting React Server...`); const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm'; const devServer = spawn(npmCmd, ['start'], { stdio: ['ignore', 'pipe', 'pipe'], // On écoute les logs pour détecter le démarrage env: { ...process.env, BROWSER: 'none' } // On empêche d'ouvrir un onglet navigateur inutile }); // On affiche les logs React au développeur devServer.stdout.pipe(process.stdout); devServer.stderr.pipe(process.stderr); let tunnelStarted = false; // 2. Écoute des logs pour lancer le tunnel au bon moment devServer.stdout.on('data', async (data) => { const output = data.toString(); // Détection : React est prêt quand il affiche "Local:" ou "Compiled" if (!tunnelStarted && (output.includes('Local:') || output.includes('Compiled successfully'))) { tunnelStarted = true; console.log(`\n[Fleetbo] 🔗 React is ready on port ${PORT}. Opening secure tunnel...`); try { // 3. Ouverture du Tunnel via API JS const url = await ngrok.connect({ addr: PORT, // On ne met pas l'authtoken ici, il le lit depuis la config globale de l'OS }); await syncFirebase(keyApp, url); } catch (err) { console.error('\n[Fleetbo] ❌ NGROK CONNECTION FAILED'); // Gestion spécifique de l'erreur d'Auth (Code 4018 ou msg string) if (err.message.includes('ERR_NGROK_4018') || err.message.includes('limited') || err.message.includes('authtoken')) { console.error('-------------------------------------------------------'); console.error('⚠️ MISSING OR INVALID AUTH TOKEN'); console.error(' Ngrok now requires a free token to verify the tunnel.'); console.error(' 1. Get your token: https://dashboard.ngrok.com/get-started/your-authtoken'); console.error(' 2. Run this command once:'); console.error(' npx ngrok config add-authtoken <YOUR_TOKEN>'); console.error('-------------------------------------------------------'); } else { console.error('Details:', err.message); } // On tue tout si le tunnel échoue, car l'app est inutilisable sans cleanupAndExit(1); } } }); } runGuardian(); // --- IMPORTS {/*const { spawn } = require('child_process'); const fs = require('fs'); const path = require('path'); const axios = require('axios'); const inquirer = require('inquirer'); const dotenv = require('dotenv'); const UPDATE_NETWORK_URL = 'https://us-central1-myapp-259bf.cloudfunctions.net/updateDeveloperNetwork'; runGuardian(); async function runGuardian() { console.log('[Fleetbo] Starting development server...'); const envPath = path.join(process.cwd(), '.env'); if (!fs.existsSync(envPath)) { console.error('Error: .env file not found.'); console.error('Please run "fleetbo start" at the root of your Fleetbo project.'); process.exit(1); } dotenv.config({ path: envPath }); const keyApp = process.env.REACT_KEY_APP; if (!keyApp) { console.error('Error: REACT_KEY_APP is missing in your .env.'); process.exit(1); } console.log(`[Fleetbo] Starting server for project: ${keyApp}...`); const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm'; const child = spawn(npmCmd, ['start'], { stdio: ['inherit', 'pipe', 'pipe'] }); let networkUrlCaptured = false; const handleData = async (data) => { const output = data.toString(); process.stdout.write(output); if (!networkUrlCaptured) { const regex = /On Your Network:\s+(http:\/\/[^\s]+)/; const match = output.match(regex); if (match && match[1]) { networkUrlCaptured = true; const networkUrl = match[1]; try { await axios.post(UPDATE_NETWORK_URL, { keyApp: keyApp, networkUrl: networkUrl }); console.log(`[Fleetbo] Server synchronized. Happy developing! 🚀`); } catch (err) { let errorMsg = err.message; if (err.response && err.response.data && err.response.data.error) { errorMsg = err.response.data.error; } console.error(`\n[Fleetbo] FATAL ERROR: Unable to synchronize network URL.`); console.error(`[Fleetbo] Reason: ${errorMsg}`); console.error(`[Fleetbo] Stopping development server...`); child.kill('SIGTERM'); process.exit(1); } } } }; child.stdout.on('data', handleData); child.stderr.on('data', handleData); child.on('close', (code) => { if (code !== null && code !== 0) { console.log(`[Fleetbo] The development server stopped (code: ${code}).`); } }); } */}