create-fleetbo-project 1.2.13 → 1.2.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/install-react-template.js +92 -70
- package/package.json +1 -1
|
@@ -9,14 +9,13 @@ const https = require('https');
|
|
|
9
9
|
const repoOwner = 'FleetFleetbo';
|
|
10
10
|
const repoName = 'dev.fleetbo.io';
|
|
11
11
|
const branchName = 'master';
|
|
12
|
-
// URL de l'archive (On cible 'codeload' implicitement via GitHub)
|
|
13
12
|
const archiveUrl = `https://github.com/${repoOwner}/${repoName}/archive/refs/heads/${branchName}.tar.gz`;
|
|
14
13
|
const bootstrapUrl = 'https://us-central1-myapp-259bf.cloudfunctions.net/bootstrapProject';
|
|
15
14
|
|
|
16
|
-
// --- LE CONTENU DU GARDIEN (CLI.JS) ---
|
|
15
|
+
// --- LE CONTENU DU GARDIEN (CLI.JS) - VERSION BYPASS WARNING ---
|
|
17
16
|
const CLI_SCRIPT_CONTENT = `#!/usr/bin/env node
|
|
18
17
|
|
|
19
|
-
const { spawn } = require('child_process');
|
|
18
|
+
const { spawn, exec, execSync } = require('child_process');
|
|
20
19
|
const fs = require('fs');
|
|
21
20
|
const path = require('path');
|
|
22
21
|
const axios = require('axios');
|
|
@@ -25,11 +24,36 @@ const ngrok = require('ngrok');
|
|
|
25
24
|
const os = require('os');
|
|
26
25
|
|
|
27
26
|
const UPDATE_NETWORK_URL = 'https://us-central1-myapp-259bf.cloudfunctions.net/updateDeveloperNetwork';
|
|
28
|
-
const PORT = 3000;
|
|
27
|
+
const PORT = 3000;
|
|
28
|
+
const NGROK_PORT = 4040;
|
|
29
|
+
|
|
30
|
+
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
31
|
+
|
|
32
|
+
// Tueur de processus générique par port
|
|
33
|
+
function killProcessOnPort(port) {
|
|
34
|
+
try {
|
|
35
|
+
if (process.platform !== 'win32') {
|
|
36
|
+
const pid = execSync(\`lsof -ti:\${port} 2>/dev/null\`).toString().trim();
|
|
37
|
+
if (pid) {
|
|
38
|
+
const pids = pid.split('\\n').join(' ');
|
|
39
|
+
console.log(\`[Fleetbo] 🔫 Freeing port \${port} (PIDs: \${pids})...\`);
|
|
40
|
+
execSync(\`kill -9 \${pids}\`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} catch (e) {}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function forceKillNgrok() {
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
const cmd = process.platform === 'win32' ? 'taskkill /IM ngrok.exe /F' : 'pkill ngrok';
|
|
49
|
+
exec(cmd, () => resolve());
|
|
50
|
+
});
|
|
51
|
+
}
|
|
29
52
|
|
|
30
53
|
async function cleanupAndExit(code = 0) {
|
|
31
|
-
console.log('\\n[Fleetbo] 🛑
|
|
32
|
-
try { await ngrok.kill(); } catch
|
|
54
|
+
console.log('\\n[Fleetbo] 🛑 Stopping services...');
|
|
55
|
+
try { await ngrok.kill(); } catch(e){}
|
|
56
|
+
killProcessOnPort(PORT);
|
|
33
57
|
process.exit(code);
|
|
34
58
|
}
|
|
35
59
|
|
|
@@ -38,10 +62,13 @@ process.on('SIGTERM', () => cleanupAndExit(0));
|
|
|
38
62
|
|
|
39
63
|
async function syncFirebase(keyApp, networkUrl) {
|
|
40
64
|
try {
|
|
65
|
+
// Si on utilise l'auth basique, l'URL inclut user:pass@... pour faciliter l'accès si besoin
|
|
66
|
+
// Mais pour le simulateur, mieux vaut envoyer l'URL standard et laisser le navigateur demander.
|
|
41
67
|
await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl });
|
|
42
68
|
console.log('\\n[Fleetbo] ---------------------------------------------------');
|
|
43
69
|
console.log(\`[Fleetbo] ✅ Tunnel Active: \${networkUrl}\`);
|
|
44
70
|
console.log(\`[Fleetbo] 🚀 Simulator: https://fleetbo.io/studio/view/\${keyApp}\`);
|
|
71
|
+
console.log(\`[Fleetbo] 🔑 Credentials: User="fleetbo", Pass="admin"\`); // RAPPEL POUR LE DEV
|
|
45
72
|
console.log('[Fleetbo] ---------------------------------------------------\\n');
|
|
46
73
|
} catch (err) {
|
|
47
74
|
console.error(\`[Fleetbo] ⚠️ Sync Error: \${err.message}\`);
|
|
@@ -51,6 +78,12 @@ async function syncFirebase(keyApp, networkUrl) {
|
|
|
51
78
|
async function runGuardian() {
|
|
52
79
|
console.log(\`[Fleetbo] 🛡️ Starting Fleetbo Guardian on \${os.platform()}...\`);
|
|
53
80
|
|
|
81
|
+
console.log(\`[Fleetbo] 🧹 Preparing environment...\`);
|
|
82
|
+
await forceKillNgrok();
|
|
83
|
+
killProcessOnPort(NGROK_PORT);
|
|
84
|
+
killProcessOnPort(PORT);
|
|
85
|
+
await sleep(1000);
|
|
86
|
+
|
|
54
87
|
const envPath = path.join(process.cwd(), '.env');
|
|
55
88
|
if (!fs.existsSync(envPath)) {
|
|
56
89
|
console.error('Error: .env file not found.');
|
|
@@ -59,13 +92,21 @@ async function runGuardian() {
|
|
|
59
92
|
dotenv.config({ path: envPath });
|
|
60
93
|
|
|
61
94
|
const keyApp = process.env.REACT_KEY_APP;
|
|
95
|
+
const authToken = process.env.NGROK_AUTHTOKEN;
|
|
62
96
|
|
|
63
|
-
|
|
97
|
+
if (authToken) {
|
|
98
|
+
try {
|
|
99
|
+
await ngrok.authtoken(authToken);
|
|
100
|
+
await sleep(500);
|
|
101
|
+
} catch (e) {}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.log(\`[Fleetbo] 📦 Booting React Server on port \${PORT}...\`);
|
|
64
105
|
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
65
106
|
|
|
66
107
|
const devServer = spawn(npmCmd, ['start'], {
|
|
67
108
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
68
|
-
env: { ...process.env, BROWSER: 'none' }
|
|
109
|
+
env: { ...process.env, BROWSER: 'none', PORT: PORT.toString() }
|
|
69
110
|
});
|
|
70
111
|
|
|
71
112
|
devServer.stdout.pipe(process.stdout);
|
|
@@ -75,23 +116,34 @@ async function runGuardian() {
|
|
|
75
116
|
|
|
76
117
|
devServer.stdout.on('data', async (data) => {
|
|
77
118
|
const output = data.toString();
|
|
119
|
+
|
|
78
120
|
if (!tunnelStarted && (output.includes('Local:') || output.includes('Compiled successfully'))) {
|
|
79
121
|
tunnelStarted = true;
|
|
80
|
-
console.log(\`\\n[Fleetbo] 🔗 React is ready.
|
|
122
|
+
console.log(\`\\n[Fleetbo] 🔗 React is ready. Initiating Tunnel...\`);
|
|
81
123
|
|
|
82
124
|
try {
|
|
83
|
-
const url = await ngrok.connect({
|
|
125
|
+
const url = await ngrok.connect({
|
|
126
|
+
addr: PORT,
|
|
127
|
+
name: \`fleetbo_\${Date.now()}\`,
|
|
128
|
+
auth: "fleetbo:admin" // <--- C'EST ICI QUE C'EST DÉFINI
|
|
129
|
+
});
|
|
84
130
|
await syncFirebase(keyApp, url);
|
|
85
|
-
} catch (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
131
|
+
} catch (firstErr) {
|
|
132
|
+
try {
|
|
133
|
+
await sleep(2000);
|
|
134
|
+
const urlRetry = await ngrok.connect({
|
|
135
|
+
addr: PORT,
|
|
136
|
+
name: \`fleetbo_retry_\${Date.now()}\`,
|
|
137
|
+
auth: "fleetbo:admin" // Retry avec Auth aussi
|
|
138
|
+
});
|
|
139
|
+
await syncFirebase(keyApp, urlRetry);
|
|
140
|
+
} catch (finalErr) {
|
|
141
|
+
console.error('\\n[Fleetbo] ❌ NGROK ERROR');
|
|
142
|
+
console.error("Message:", finalErr.message);
|
|
143
|
+
if (finalErr.message.includes('ERR_NGROK_4018')) {
|
|
144
|
+
console.error("👉 Add NGROK_AUTHTOKEN=... to your .env file OR run 'npx ngrok config add-authtoken <TOKEN>'");
|
|
145
|
+
}
|
|
93
146
|
}
|
|
94
|
-
cleanupAndExit(1);
|
|
95
147
|
}
|
|
96
148
|
}
|
|
97
149
|
});
|
|
@@ -105,29 +157,25 @@ const args = process.argv.slice(2);
|
|
|
105
157
|
const projectNameArg = args.find(arg => !arg.startsWith('--'));
|
|
106
158
|
const tokenArg = args.find(arg => arg.startsWith('--token='));
|
|
107
159
|
const bootstrapTokenArg = tokenArg ? tokenArg.split('=')[1] : null;
|
|
160
|
+
const ngrokArg = args.find(arg => arg.startsWith('--ngrok='));
|
|
161
|
+
const ngrokTokenArg = ngrokArg ? ngrokArg.split('=')[1] : null;
|
|
108
162
|
|
|
109
163
|
if (!projectNameArg || !bootstrapTokenArg) {
|
|
110
|
-
console.error('\n ❌ Usage: npx create-fleetbo-project <
|
|
164
|
+
console.error('\n ❌ Usage: npx create-fleetbo-project <name> --token=<fleetbo-token> [--ngrok=<ngrok-token>]');
|
|
111
165
|
process.exit(1);
|
|
112
166
|
}
|
|
113
167
|
|
|
114
168
|
const projectName = projectNameArg;
|
|
115
169
|
const projectDir = path.join(process.cwd(), projectName);
|
|
116
170
|
|
|
117
|
-
|
|
118
171
|
// --- Fonctions Utilitaires ---
|
|
119
172
|
function fetchProjectKeys(token) {
|
|
120
173
|
return new Promise((resolve, reject) => {
|
|
121
174
|
const postData = JSON.stringify({ token });
|
|
122
175
|
const uri = new URL(bootstrapUrl);
|
|
123
176
|
const options = {
|
|
124
|
-
hostname: uri.hostname,
|
|
125
|
-
|
|
126
|
-
method: 'POST',
|
|
127
|
-
headers: {
|
|
128
|
-
'Content-Type': 'application/json',
|
|
129
|
-
'Content-Length': Buffer.byteLength(postData)
|
|
130
|
-
}
|
|
177
|
+
hostname: uri.hostname, path: uri.pathname, method: 'POST',
|
|
178
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) }
|
|
131
179
|
};
|
|
132
180
|
const req = https.request(options, (res) => {
|
|
133
181
|
let data = '';
|
|
@@ -144,57 +192,29 @@ function fetchProjectKeys(token) {
|
|
|
144
192
|
});
|
|
145
193
|
}
|
|
146
194
|
|
|
147
|
-
// --- CORRECTION MAJEURE : Ajout du User-Agent pour éviter l'erreur 503 ---
|
|
148
195
|
function downloadEngine(url, dest) {
|
|
149
196
|
return new Promise((resolve, reject) => {
|
|
150
|
-
// On parse l'URL pour pouvoir ajouter des headers
|
|
151
197
|
const uri = new URL(url);
|
|
152
198
|
const options = {
|
|
153
|
-
hostname: uri.hostname,
|
|
154
|
-
|
|
155
|
-
method: 'GET',
|
|
156
|
-
headers: {
|
|
157
|
-
'User-Agent': 'Fleetbo-CLI-Installer', // CRITIQUE POUR GITHUB
|
|
158
|
-
'Accept': '*/*'
|
|
159
|
-
}
|
|
199
|
+
hostname: uri.hostname, path: uri.pathname + uri.search, method: 'GET',
|
|
200
|
+
headers: { 'User-Agent': 'Fleetbo-CLI-Installer', 'Accept': '*/*' }
|
|
160
201
|
};
|
|
161
|
-
|
|
162
202
|
const request = https.get(options, (response) => {
|
|
163
|
-
// Gestion Redirection (301/302)
|
|
164
203
|
if (response.statusCode === 301 || response.statusCode === 302) {
|
|
165
204
|
if (!response.headers.location) {
|
|
166
205
|
reject(new Error("Redirect found but no location header"));
|
|
167
206
|
return;
|
|
168
207
|
}
|
|
169
|
-
// Appel récursif avec la nouvelle URL
|
|
170
208
|
downloadEngine(response.headers.location, dest).then(resolve).catch(reject);
|
|
171
209
|
return;
|
|
172
210
|
}
|
|
173
|
-
|
|
174
|
-
// Vérification du statut
|
|
175
|
-
if (response.statusCode !== 200) {
|
|
176
|
-
reject(new Error(`Failed to download (Status: ${response.statusCode})`));
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Écriture du fichier
|
|
211
|
+
if (response.statusCode !== 200) { reject(new Error(`Status: ${response.statusCode}`)); return; }
|
|
181
212
|
const file = fs.createWriteStream(dest);
|
|
182
213
|
response.pipe(file);
|
|
183
|
-
|
|
184
|
-
file.on('
|
|
185
|
-
file.close(resolve);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
file.on('error', (err) => {
|
|
189
|
-
fs.unlink(dest, () => {}); // Supprimer le fichier incomplet
|
|
190
|
-
reject(err);
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
request.on('error', (err) => {
|
|
195
|
-
fs.unlink(dest, () => {});
|
|
196
|
-
reject(err);
|
|
214
|
+
file.on('finish', () => file.close(resolve));
|
|
215
|
+
file.on('error', (err) => { fs.unlink(dest, () => {}); reject(err); });
|
|
197
216
|
});
|
|
217
|
+
request.on('error', (err) => reject(err));
|
|
198
218
|
});
|
|
199
219
|
}
|
|
200
220
|
|
|
@@ -206,12 +226,10 @@ async function setupProject() {
|
|
|
206
226
|
if (fs.existsSync(projectDir)) throw new Error(`Directory "${projectName}" already exists.`);
|
|
207
227
|
fs.mkdirSync(projectDir);
|
|
208
228
|
|
|
209
|
-
// 1. Download
|
|
210
229
|
console.log(' [1/6] 📥 Downloading Fleetbo Core Engine...');
|
|
211
230
|
const archivePath = path.join(projectDir, 'engine.tar.gz');
|
|
212
231
|
await downloadEngine(archiveUrl, archivePath);
|
|
213
232
|
|
|
214
|
-
// 2. Extract
|
|
215
233
|
console.log(' [2/6] 📦 Scaffolding project structure...');
|
|
216
234
|
try {
|
|
217
235
|
execSync(`tar -xf "${archivePath}" -C "${projectDir}" --strip-components=1`, { stdio: 'ignore' });
|
|
@@ -220,23 +238,25 @@ async function setupProject() {
|
|
|
220
238
|
|
|
221
239
|
process.chdir(projectDir);
|
|
222
240
|
|
|
223
|
-
// 3. Auth
|
|
224
241
|
console.log(' [3/6] 🔑 Authenticating with Fleetbo Cloud...');
|
|
225
242
|
const keys = await fetchProjectKeys(bootstrapTokenArg);
|
|
226
243
|
if (!keys.enterpriseId) throw new Error("Invalid keys.");
|
|
227
244
|
|
|
228
|
-
// 4. Environment & CLI Generation
|
|
229
245
|
console.log(' [4/6] ⚙️ Configuring environment & CLI...');
|
|
230
|
-
|
|
246
|
+
let envContent = `REACT_APP_FLEETBO_DB_KEY=${keys.fleetboDBKey}\nREACT_APP_ENTERPRISE_ID=${keys.enterpriseId}\nREACT_KEY_APP=${projectName}\nDANGEROUSLY_DISABLE_HOST_CHECK=true\n`;
|
|
247
|
+
if (ngrokTokenArg) {
|
|
248
|
+
console.log(' ✅ Ngrok token injected into .env');
|
|
249
|
+
envContent += `NGROK_AUTHTOKEN=${ngrokTokenArg}\n`;
|
|
250
|
+
} else {
|
|
251
|
+
console.log(' ℹ️ No Ngrok token provided. Assuming global config.');
|
|
252
|
+
}
|
|
231
253
|
fs.writeFileSync(path.join(projectDir, '.env'), envContent, 'utf8');
|
|
232
254
|
fs.writeFileSync(path.join(projectDir, 'cli.js'), CLI_SCRIPT_CONTENT, 'utf8');
|
|
233
255
|
|
|
234
|
-
// 5. Deps
|
|
235
256
|
console.log(' [5/6] 📚 Installing dependencies...');
|
|
236
257
|
execSync('npm install', { stdio: 'inherit' });
|
|
237
258
|
execSync('npm install ngrok dotenv axios --save-dev', { stdio: 'ignore' });
|
|
238
259
|
|
|
239
|
-
// 6. Finalize
|
|
240
260
|
console.log(' [6/6] ✨ Finalizing setup...');
|
|
241
261
|
const packageJsonPath = path.join(projectDir, 'package.json');
|
|
242
262
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
@@ -244,7 +264,7 @@ async function setupProject() {
|
|
|
244
264
|
packageJson.scripts = { ...packageJson.scripts, "fleetbo": "node cli.js", "dev": "node cli.js" };
|
|
245
265
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf8');
|
|
246
266
|
|
|
247
|
-
console.log('\n
|
|
267
|
+
console.log('\n [Fleetbo] ✅ Project successfully created!');
|
|
248
268
|
console.log(`\n👉 Run: cd ${projectName} && npm run fleetbo`);
|
|
249
269
|
|
|
250
270
|
} catch (error) {
|
|
@@ -258,6 +278,8 @@ setupProject();
|
|
|
258
278
|
|
|
259
279
|
|
|
260
280
|
|
|
281
|
+
|
|
282
|
+
|
|
261
283
|
{/*
|
|
262
284
|
|
|
263
285
|
const { execSync } = require('child_process');
|