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.
- package/install-react-template.js +62 -122
- package/package.json +1 -1
|
@@ -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
|
|
15
|
+
// --- LE GARDIEN CLOUDFLARE (Version Production Silencieuse) ---
|
|
16
16
|
const CLI_SCRIPT_CONTENT = `#!/usr/bin/env node
|
|
17
17
|
|
|
18
|
-
const { spawn,
|
|
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
|
-
|
|
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
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const cmd = process.platform === 'win32' ? 'taskkill /IM
|
|
35
|
-
|
|
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
|
|
41
|
-
|
|
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
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
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',
|
|
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.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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=<
|
|
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
|
-
|
|
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
|
-
|
|
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('
|
|
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("
|
|
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
|
-
|
|
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
|
-
|
|
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');
|