fleetbo-cli
Version:
Creates a new Fleetbo project.
223 lines (188 loc) • 9.42 kB
JavaScript
// --- IMPORTS (Anciens + Nouveaux) ---
const { execSync, spawn } = require('child_process'); // 'spawn' est ajouté
const fs = require('fs');
const path = require('path');
const https = require('https');
const axios = require('axios'); // 'axios' est ajouté (plus propre que https)
const inquirer = require('inquirer'); // 'inquirer' est ajouté
const dotenv = require('dotenv'); // 'dotenv' est ajouté
// --- URLs de vos Cloud Functions ---
const BOOTSTRAP_URL = 'https://us-central1-myapp-259bf.cloudfunctions.net/bootstrapProject';
// Mettez à jour avec vos vraies URLs de déploiement
const CHECK_ACCESS_URL = 'https://us-central1-myapp-259bf.cloudfunctions.net/checkDeveloperAccess';
const UPDATE_NETWORK_URL = 'https://us-central1-myapp-259bf.cloudfunctions.net/updateDeveloperNetwork';
// --- LE ROUTEUR ---
// Il décide quelle fonction lancer
const args = process.argv.slice(2);
const command = args[0]; // ex: 'start' ou 'mon-projet'
if (command === 'start') {
runGuardian(); // Lancer le "gardien"
} else {
// Si ce n'est pas 'start', on suppose que c'est une installation.
// L'ancienne logique 'setupProject' sera appelée.
setupProject(args);
}
// ==========================================================
// FONCTION 1: LE "GARDIEN" (Nouveau code pour `fleetbo start`)
// ==========================================================
async function runGuardian() {
console.log('[Fleetbo] Lancement du serveur de développement...');
// 1. Charger le fichier .env du PROJET UTILISATEUR
const envPath = path.join(process.cwd(), '.env');
if (!fs.existsSync(envPath)) {
console.error('Erreur: Fichier .env introuvable.');
console.error('Veuillez lancer "fleetbo start" à la racine de votre projet Fleetbo.');
process.exit(1);
}
dotenv.config({ path: envPath }); // Charge les variables de .env
const enterpriseId = process.env.REACT_APP_ENTERPRISE_ID;
if (!enterpriseId) {
console.error('Erreur: REACT_APP_ENTERPRISE_ID est manquant dans votre .env.');
process.exit(1);
}
// 2. Demander l'e-mail
const answers = await inquirer.prompt([
{ type: 'input', name: 'email', message: 'Veuillez entrer votre e-mail (admin/testeur) :' }
]);
const email = answers.email.trim();
// 3. Vérifier l'accès via la Cloud Function (SÉCURISÉ)
try {
console.log(`Vérification de l'accès pour ${email}...`);
// APPEL SÉCURISÉ
await axios.post(CHECK_ACCESS_URL, {
email: email,
enterpriseId: enterpriseId
});
console.log('Accès autorisé. Démarrage du serveur...');
} catch (error) {
console.error('\nAccès refusé.');
if (error.response && error.response.data) {
console.error(`Raison: ${error.response.data.error}`);
} else {
console.error(error.message);
}
process.exit(1);
}
// 4. Lancer "npm start" (du package.json local de l'utilisateur)
// Le template 'dev.fleetbo.io' doit avoir "start": "react-scripts start"
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
const child = spawn(npmCmd, ['start'], { stdio: ['inherit', 'pipe', 'inherit'] });
// 5. Capturer l'URL du réseau
child.stdout.on('data', (data) => {
const output = data.toString();
process.stdout.write(output); // Relayer la sortie pour que l'utilisateur la voie
const regex = /On Your Network:\s+(http:\/\/[^\s]+)/;
const match = output.match(regex);
if (match && match[1]) {
const networkUrl = match[1];
console.log(`\n[Fleetbo] URL réseau capturée : ${networkUrl}`);
// 6. Mettre à jour Firestore via Cloud Function (en "fire-and-forget")
axios.post(UPDATE_NETWORK_URL, {
enterpriseId: enterpriseId,
networkUrl: networkUrl
}).catch(err => {
// Pas critique si ça échoue, on affiche juste un avertissement
console.warn('[Fleetbo] Avertissement: Impossible de mettre à jour l\'URL réseau.', err.message);
});
}
});
}
// =================================================================
// FONCTION 2: L'"INSTALLATEUR" (Votre code existant, adapté)
// =================================================================
// On passe 'args' à la fonction
async function setupProject(installerArgs) {
// --- Configuration de l'Installateur (Votre code) ---
const repoOwner = 'FleetFleetbo';
const repoName = 'dev.fleetbo.io';
const branchName = 'master';
const repoGitUrl = `https://github.com/${repoOwner}/${repoName}.git`;
// bootstrapUrl est déjà défini en haut
// --- Analyse des Arguments (Adapté) ---
// On utilise 'installerArgs' au lieu de 'process.argv.slice(2)'
const projectNameArg = installerArgs.find(arg => !arg.startsWith('--'));
const tokenArg = installerArgs.find(arg => arg.startsWith('--token='));
// Le reste de vos vérifications est parfait
if (!projectNameArg) {
console.error('\n Error : Please specify a name for your project.');
console.log(' Usage: npx fleetbo <nom-du-projet> --token=<votre-token>'); // 'create-fleetbo-project' devient 'fleetbo'
process.exit(1);
}
const bootstrapToken = tokenArg ? tokenArg.split('=')[1] : null;
if (!bootstrapToken) {
console.error('\n Error : "The bootstrap token is missing.');
console.log(' Usage: npx fleetbo <nom-du-projet> --token=<votre-token>');
process.exit(1);
}
const projectName = projectNameArg;
// --- Fonction Principale (Votre code, inchangé) ---
console.log(`\nCreating your Fleetbo project "${projectName}"...`);
const projectDir = path.join(process.cwd(), projectName);
try {
// Étape 1 : Télécharger
console.log(' [1/5] Initializing project structure...');
execSync(`git clone --depth 1 --branch ${branchName} ${repoGitUrl} "${projectName}" 2> /dev/null`);
// Étape 2 : Nettoyer
console.log(' [2/5] Project structure initialized. Configuring...');
process.chdir(projectDir);
fs.rmSync(path.join(projectDir, '.git'), { recursive: true, force: true });
// Étape 3 : Récupération des clés (utilise 'fetchProjectKeys' ci-dessous)
console.log(' [3/5] Fetching project keys...');
const keys = await fetchProjectKeys(bootstrapToken);
if (!keys.enterpriseId || !keys.fleetboDBKey) {
throw new Error("Received keys from the server are invalid.");
}
// Étape 4 : Configuration du .env
console.log(' [4/5] .env file configured successfully.');
const envContent = `REACT_APP_FLEETBO_DB_KEY=${keys.fleetboDBKey}\nREACT_APP_ENTERPRISE_ID=${keys.enterpriseId}\n`; // J'ai remis la variable enterpriseId
fs.writeFileSync(path.join(projectDir, '.env'), envContent, 'utf8');
// Étape 5 : Installation
console.log(' [5/5] Installing dependencies...');
execSync('npm install', { stdio: 'inherit' });
// Personnalisation
const packageJsonPath = path.join(projectDir, 'package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
packageJson.name = projectName;
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf8');
console.log('\n🚀 Your Fleetbo project is ready !');
console.log(`\nTo get started, run the following commands :`);
console.log(` cd ${projectName}`);
console.log(` npx fleetbo start`); // <-- CHANGEMENT IMPORTANT !
} catch (error) {
console.error('\n An error occurred while creating the project :', error.message);
if (fs.existsSync(projectDir)) {
fs.rmSync(projectDir, { recursive: true, force: true });
}
}
}
// --- Fonctions Utilitaires (Votre code, inchangé) ---
function fetchProjectKeys(token) {
return new Promise((resolve, reject) => {
const postData = JSON.stringify({ token });
const options = { method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) } };
// 'bootstrapUrl' est défini en haut
const req = https.request(BOOTSTRAP_URL, options, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
try {
// On log les clés pour le debug
const parsedData = JSON.parse(data);
console.log('Keys received:', parsedData);
resolve(parsedData);
} catch (e) {
reject(new Error('Invalid response from the key server.'));
}
} else {
const errorMsg = JSON.parse(data).error || `Server error (code: ${res.statusCode})`;
reject(new Error(errorMsg));
}
});
});
req.on('error', (e) => reject(e));
req.write(postData);
req.end();
});
}