create-fleetbo-project 1.2.14 → 1.2.18

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.
@@ -12,33 +12,41 @@ const branchName = 'master';
12
12
  const archiveUrl = `https://github.com/${repoOwner}/${repoName}/archive/refs/heads/${branchName}.tar.gz`;
13
13
  const bootstrapUrl = 'https://us-central1-myapp-259bf.cloudfunctions.net/bootstrapProject';
14
14
 
15
- // --- LE CONTENU DU GARDIEN (CLI.JS) - VERSION BLINDÉE ---
15
+ // --- LE GARDIEN CLOUDFLARE (Version Production Silencieuse) ---
16
16
  const CLI_SCRIPT_CONTENT = `#!/usr/bin/env node
17
17
 
18
- const { spawn, exec } = require('child_process');
18
+ const { spawn, execSync } = require('child_process');
19
19
  const fs = require('fs');
20
20
  const path = require('path');
21
21
  const axios = require('axios');
22
22
  const dotenv = require('dotenv');
23
- const ngrok = require('ngrok');
24
23
  const os = require('os');
25
24
 
26
25
  const UPDATE_NETWORK_URL = 'https://us-central1-myapp-259bf.cloudfunctions.net/updateDeveloperNetwork';
27
26
  const PORT = 3000;
28
27
 
29
- const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
28
+ // Nettoyage des ports (Mac/Linux)
29
+ function killProcessOnPort(port) {
30
+ try {
31
+ if (process.platform !== 'win32') {
32
+ const pid = execSync(\`lsof -ti:\${port} 2>/dev/null\`).toString().trim();
33
+ if (pid) execSync(\`kill -9 \${pid.split('\\n').join(' ')} 2>/dev/null\`);
34
+ }
35
+ } catch (e) {}
36
+ }
30
37
 
31
- // 1. NETTOYAGE VIOLENT (Anti-Zombie)
32
- async function forceKillNgrok() {
33
- return new Promise((resolve) => {
34
- const cmd = process.platform === 'win32' ? 'taskkill /IM ngrok.exe /F' : 'pkill ngrok';
35
- exec(cmd, () => resolve());
36
- });
38
+ // Nettoyage spécifique Cloudflare
39
+ function killCloudflared() {
40
+ try {
41
+ const cmd = process.platform === 'win32' ? 'taskkill /IM cloudflared.exe /F' : 'pkill cloudflared';
42
+ execSync(cmd + ' 2>/dev/null');
43
+ } catch (e) {}
37
44
  }
38
45
 
39
46
  async function cleanupAndExit(code = 0) {
40
- console.log('\\n[Fleetbo] 🛑 Stopping React...');
41
- try { await ngrok.kill(); } catch(e){}
47
+ console.log('\\n[Fleetbo] 🛑 Stopping services...');
48
+ killCloudflared();
49
+ killProcessOnPort(PORT);
42
50
  process.exit(code);
43
51
  }
44
52
 
@@ -60,10 +68,9 @@ async function syncFirebase(keyApp, networkUrl) {
60
68
  async function runGuardian() {
61
69
  console.log(\`[Fleetbo] 🛡️ Starting Fleetbo Guardian on \${os.platform()}...\`);
62
70
 
63
- // 2. NETTOYAGE AU DÉMARRAGE
64
- console.log(\`[Fleetbo] 🧹 Cleaning up...\`);
65
- await forceKillNgrok();
66
- await sleep(1000);
71
+ // 1. NETTOYAGE PRÉVENTIF
72
+ killCloudflared();
73
+ killProcessOnPort(PORT);
67
74
 
68
75
  const envPath = path.join(process.cwd(), '.env');
69
76
  if (!fs.existsSync(envPath)) {
@@ -71,24 +78,15 @@ async function runGuardian() {
71
78
  process.exit(1);
72
79
  }
73
80
  dotenv.config({ path: envPath });
74
-
75
81
  const keyApp = process.env.REACT_KEY_APP;
76
- const authToken = process.env.NGROK_AUTHTOKEN;
77
-
78
- // 3. AUTHENTIFICATION (Si token présent dans .env, sinon Global)
79
- if (authToken) {
80
- try {
81
- await ngrok.authtoken(authToken);
82
- await sleep(1000);
83
- } catch (e) {}
84
- }
85
82
 
83
+ // 2. DÉMARRAGE REACT
86
84
  console.log(\`[Fleetbo] 📦 Booting React Server...\`);
87
85
  const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
88
86
 
89
87
  const devServer = spawn(npmCmd, ['start'], {
90
88
  stdio: ['ignore', 'pipe', 'pipe'],
91
- env: { ...process.env, BROWSER: 'none' }
89
+ env: { ...process.env, BROWSER: 'none', PORT: PORT.toString() }
92
90
  });
93
91
 
94
92
  devServer.stdout.pipe(process.stdout);
@@ -96,37 +94,33 @@ async function runGuardian() {
96
94
 
97
95
  let tunnelStarted = false;
98
96
 
99
- devServer.stdout.on('data', async (data) => {
97
+ devServer.stdout.on('data', (data) => {
100
98
  const output = data.toString();
101
99
 
102
100
  if (!tunnelStarted && (output.includes('Local:') || output.includes('Compiled successfully'))) {
103
101
  tunnelStarted = true;
104
- console.log(\`\\n[Fleetbo] 🔗 React is ready. Initiating Tunnel...\`);
105
-
106
- try {
107
- // 4. CONNEXION AVEC NOM UNIQUE (Évite les collisions)
108
- const url = await ngrok.connect({
109
- addr: PORT,
110
- name: \`fleetbo_\${Date.now()}\` // <--- CRUCIAL
111
- });
112
- await syncFirebase(keyApp, url);
113
- } catch (firstErr) {
114
- // 5. RETRY LOGIC
115
- try {
116
- await sleep(2000);
117
- const urlRetry = await ngrok.connect({
118
- addr: PORT,
119
- name: \`fleetbo_retry_\${Date.now()}\`
120
- });
121
- await syncFirebase(keyApp, urlRetry);
122
- } catch (finalErr) {
123
- console.error('\\n[Fleetbo] ❌ NGROK ERROR');
124
- console.error("Message:", finalErr.message);
125
- if (finalErr.message.includes('ERR_NGROK_4018')) {
126
- console.error("👉 Add NGROK_AUTHTOKEN=... to your .env file OR run 'npx ngrok config add-authtoken <TOKEN>'");
102
+ console.log(\`\\n[Fleetbo] 🔗 React is ready. Establishing secure tunnel...\`);
103
+
104
+ // 3. DÉMARRAGE TUNNEL CLOUDFLARE (via npm exec pour la compatibilité)
105
+ const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';
106
+ const tunnel = spawn(npxCmd, ['cloudflared', 'tunnel', '--url', \`http://localhost:\${PORT}\`]);
107
+
108
+ // Capture de l'URL dans les logs
109
+ tunnel.stderr.on('data', (chunk) => {
110
+ const log = chunk.toString();
111
+ const match = log.match(/https:\/\/[a-zA-Z0-9-]+\.trycloudflare\.com/);
112
+ if (match) {
113
+ const url = match[0];
114
+ if (global.currentUrl !== url) {
115
+ global.currentUrl = url;
116
+ syncFirebase(keyApp, url);
127
117
  }
128
118
  }
129
- }
119
+ });
120
+
121
+ tunnel.on('error', (err) => {
122
+ console.error("[Fleetbo] ❌ Tunnel Error. Ensure 'cloudflared' is installed.");
123
+ });
130
124
  }
131
125
  });
132
126
  }
@@ -137,37 +131,25 @@ runGuardian();
137
131
  // --- Analyse des Arguments ---
138
132
  const args = process.argv.slice(2);
139
133
  const projectNameArg = args.find(arg => !arg.startsWith('--'));
140
-
141
- // Argument Token Fleetbo
142
134
  const tokenArg = args.find(arg => arg.startsWith('--token='));
143
135
  const bootstrapTokenArg = tokenArg ? tokenArg.split('=')[1] : null;
144
136
 
145
- // --- NOUVEAU : Argument Token Ngrok (Optionnel) ---
146
- const ngrokArg = args.find(arg => arg.startsWith('--ngrok='));
147
- const ngrokTokenArg = ngrokArg ? ngrokArg.split('=')[1] : null;
148
-
149
137
  if (!projectNameArg || !bootstrapTokenArg) {
150
- console.error('\n ❌ Usage: npx create-fleetbo-project <name> --token=<fleetbo-token> [--ngrok=<ngrok-token>]');
138
+ console.error('\n ❌ Usage: npx create-fleetbo-project <name> --token=<token>');
151
139
  process.exit(1);
152
140
  }
153
141
 
154
142
  const projectName = projectNameArg;
155
143
  const projectDir = path.join(process.cwd(), projectName);
156
144
 
157
-
158
145
  // --- Fonctions Utilitaires ---
159
146
  function fetchProjectKeys(token) {
160
147
  return new Promise((resolve, reject) => {
161
148
  const postData = JSON.stringify({ token });
162
149
  const uri = new URL(bootstrapUrl);
163
150
  const options = {
164
- hostname: uri.hostname,
165
- path: uri.pathname,
166
- method: 'POST',
167
- headers: {
168
- 'Content-Type': 'application/json',
169
- 'Content-Length': Buffer.byteLength(postData)
170
- }
151
+ hostname: uri.hostname, path: uri.pathname, method: 'POST',
152
+ headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) }
171
153
  };
172
154
  const req = https.request(options, (res) => {
173
155
  let data = '';
@@ -188,47 +170,22 @@ function downloadEngine(url, dest) {
188
170
  return new Promise((resolve, reject) => {
189
171
  const uri = new URL(url);
190
172
  const options = {
191
- hostname: uri.hostname,
192
- path: uri.pathname + uri.search,
193
- method: 'GET',
194
- headers: {
195
- 'User-Agent': 'Fleetbo-CLI-Installer',
196
- 'Accept': '*/*'
197
- }
173
+ hostname: uri.hostname, path: uri.pathname + uri.search, method: 'GET',
174
+ headers: { 'User-Agent': 'Fleetbo-CLI-Installer', 'Accept': '*/*' }
198
175
  };
199
-
200
176
  const request = https.get(options, (response) => {
201
177
  if (response.statusCode === 301 || response.statusCode === 302) {
202
- if (!response.headers.location) {
203
- reject(new Error("Redirect found but no location header"));
204
- return;
205
- }
178
+ if (!response.headers.location) { reject(new Error("Redirect error")); return; }
206
179
  downloadEngine(response.headers.location, dest).then(resolve).catch(reject);
207
180
  return;
208
181
  }
209
-
210
- if (response.statusCode !== 200) {
211
- reject(new Error(`Failed to download (Status: ${response.statusCode})`));
212
- return;
213
- }
214
-
182
+ if (response.statusCode !== 200) { reject(new Error(`Status: ${response.statusCode}`)); return; }
215
183
  const file = fs.createWriteStream(dest);
216
184
  response.pipe(file);
217
-
218
- file.on('finish', () => {
219
- file.close(resolve);
220
- });
221
-
222
- file.on('error', (err) => {
223
- fs.unlink(dest, () => {});
224
- reject(err);
225
- });
226
- });
227
-
228
- request.on('error', (err) => {
229
- fs.unlink(dest, () => {});
230
- reject(err);
185
+ file.on('finish', () => file.close(resolve));
186
+ file.on('error', (err) => { fs.unlink(dest, () => {}); reject(err); });
231
187
  });
188
+ request.on('error', (err) => reject(err));
232
189
  });
233
190
  }
234
191
 
@@ -240,51 +197,35 @@ async function setupProject() {
240
197
  if (fs.existsSync(projectDir)) throw new Error(`Directory "${projectName}" already exists.`);
241
198
  fs.mkdirSync(projectDir);
242
199
 
243
- // 1. Download
244
200
  console.log(' [1/6] 📥 Downloading Fleetbo Core Engine...');
245
201
  const archivePath = path.join(projectDir, 'engine.tar.gz');
246
202
  await downloadEngine(archiveUrl, archivePath);
247
203
 
248
- // 2. Extract
249
204
  console.log(' [2/6] 📦 Scaffolding project structure...');
250
205
  try {
251
206
  execSync(`tar -xf "${archivePath}" -C "${projectDir}" --strip-components=1`, { stdio: 'ignore' });
252
207
  fs.unlinkSync(archivePath);
253
- } catch (e) { throw new Error("Failed to extract engine. Ensure 'tar' is available."); }
208
+ } catch (e) { throw new Error("Extract failed."); }
254
209
 
255
210
  process.chdir(projectDir);
256
211
 
257
- // 3. Auth
258
212
  console.log(' [3/6] 🔑 Authenticating with Fleetbo Cloud...');
259
213
  const keys = await fetchProjectKeys(bootstrapTokenArg);
260
214
  if (!keys.enterpriseId) throw new Error("Invalid keys.");
261
215
 
262
- // 4. Environment & CLI Generation
263
216
  console.log(' [4/6] ⚙️ Configuring environment & CLI...');
264
-
265
- // --- CRÉATION DU .ENV ---
266
- let envContent = `REACT_APP_FLEETBO_DB_KEY=${keys.fleetboDBKey}\n`;
267
- envContent += `REACT_APP_ENTERPRISE_ID=${keys.enterpriseId}\n`;
268
- envContent += `REACT_KEY_APP=${projectName}\n`;
269
- envContent += `DANGEROUSLY_DISABLE_HOST_CHECK=true\n`;
270
-
271
- // Ajout conditionnel du token Ngrok
272
- if (ngrokTokenArg) {
273
- console.log(' ✅ Ngrok token injected into .env');
274
- envContent += `NGROK_AUTHTOKEN=${ngrokTokenArg}\n`;
275
- } else {
276
- console.log(' ℹ️ No Ngrok token provided. Assuming global config.');
277
- }
217
+ // On force le host check pour la compatibilité tunnel
218
+ const envContent = `REACT_APP_FLEETBO_DB_KEY=${keys.fleetboDBKey}\nREACT_APP_ENTERPRISE_ID=${keys.enterpriseId}\nREACT_KEY_APP=${projectName}\nDANGEROUSLY_DISABLE_HOST_CHECK=true\n`;
278
219
 
279
220
  fs.writeFileSync(path.join(projectDir, '.env'), envContent, 'utf8');
280
221
  fs.writeFileSync(path.join(projectDir, 'cli.js'), CLI_SCRIPT_CONTENT, 'utf8');
281
222
 
282
- // 5. Deps
283
223
  console.log(' [5/6] 📚 Installing dependencies...');
284
224
  execSync('npm install', { stdio: 'inherit' });
285
- execSync('npm install ngrok dotenv axios --save-dev', { stdio: 'ignore' });
225
+
226
+ // Installation de cloudflared
227
+ execSync('npm install cloudflared dotenv axios --save-dev', { stdio: 'ignore' });
286
228
 
287
- // 6. Finalize
288
229
  console.log(' [6/6] ✨ Finalizing setup...');
289
230
  const packageJsonPath = path.join(projectDir, 'package.json');
290
231
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
@@ -305,7 +246,6 @@ async function setupProject() {
305
246
  setupProject();
306
247
 
307
248
 
308
-
309
249
  {/*
310
250
 
311
251
  const { execSync } = require('child_process');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-fleetbo-project",
3
- "version": "1.2.14",
3
+ "version": "1.2.18",
4
4
  "description": "Creates a new Fleetbo project.",
5
5
  "main": "install-react-template.js",
6
6
  "bin": {