fleetbo
Version:
Runs and manages Fleetbo projects.
207 lines (162 loc) • 7.73 kB
JavaScript
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}).`);
}
});
}
*/}