create-fleetbo-project 1.2.10 → 1.2.12
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 +116 -95
- package/package.json +1 -1
|
@@ -4,41 +4,117 @@ const { execSync } = require('child_process');
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const https = require('https');
|
|
7
|
-
// const os = require('os'); // Pas strictement nécessaire ici
|
|
8
7
|
|
|
9
8
|
// --- Configuration ---
|
|
10
9
|
const repoOwner = 'FleetFleetbo';
|
|
11
10
|
const repoName = 'dev.fleetbo.io';
|
|
12
11
|
const branchName = 'master';
|
|
13
|
-
|
|
14
|
-
// URL de téléchargement direct de l'archive
|
|
15
12
|
const archiveUrl = `https://github.com/${repoOwner}/${repoName}/archive/refs/heads/${branchName}.tar.gz`;
|
|
16
13
|
const bootstrapUrl = 'https://us-central1-myapp-259bf.cloudfunctions.net/bootstrapProject';
|
|
17
14
|
|
|
15
|
+
// --- LE CONTENU DU GARDIEN (Injecté dynamiquement) ---
|
|
16
|
+
const CLI_SCRIPT_CONTENT = `#!/usr/bin/env node
|
|
17
|
+
|
|
18
|
+
const { spawn } = require('child_process');
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
const axios = require('axios');
|
|
22
|
+
const dotenv = require('dotenv');
|
|
23
|
+
const ngrok = require('ngrok');
|
|
24
|
+
const os = require('os');
|
|
25
|
+
|
|
26
|
+
const UPDATE_NETWORK_URL = 'https://us-central1-myapp-259bf.cloudfunctions.net/updateDeveloperNetwork';
|
|
27
|
+
const PORT = 3000;
|
|
28
|
+
|
|
29
|
+
async function cleanupAndExit(code = 0) {
|
|
30
|
+
console.log('\\n[Fleetbo] 🛑 Shutting down services...');
|
|
31
|
+
try { await ngrok.kill(); } catch (e) {}
|
|
32
|
+
process.exit(code);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
process.on('SIGINT', () => cleanupAndExit(0));
|
|
36
|
+
process.on('SIGTERM', () => cleanupAndExit(0));
|
|
37
|
+
|
|
38
|
+
async function syncFirebase(keyApp, networkUrl) {
|
|
39
|
+
try {
|
|
40
|
+
await axios.post(UPDATE_NETWORK_URL, { keyApp, networkUrl });
|
|
41
|
+
console.log('\\n[Fleetbo] ---------------------------------------------------');
|
|
42
|
+
console.log(\`[Fleetbo] ✅ Tunnel Active: \${networkUrl}\`);
|
|
43
|
+
console.log(\`[Fleetbo] 🚀 Simulator: https://fleetbo.io/studio/view/\${keyApp}\`);
|
|
44
|
+
console.log('[Fleetbo] ---------------------------------------------------\\n');
|
|
45
|
+
} catch (err) {
|
|
46
|
+
console.error(\`[Fleetbo] ⚠️ Sync Error: \${err.message}\`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function runGuardian() {
|
|
51
|
+
console.log(\`[Fleetbo] 🛡️ Starting Fleetbo Guardian on \${os.platform()}...\`);
|
|
52
|
+
|
|
53
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
54
|
+
if (!fs.existsSync(envPath)) {
|
|
55
|
+
console.error('Error: .env file not found.');
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
dotenv.config({ path: envPath });
|
|
59
|
+
|
|
60
|
+
const keyApp = process.env.REACT_KEY_APP;
|
|
61
|
+
|
|
62
|
+
console.log(\`[Fleetbo] 📦 Booting React Server...\`);
|
|
63
|
+
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
64
|
+
|
|
65
|
+
const devServer = spawn(npmCmd, ['start'], {
|
|
66
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
67
|
+
env: { ...process.env, BROWSER: 'none' }
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
devServer.stdout.pipe(process.stdout);
|
|
71
|
+
devServer.stderr.pipe(process.stderr);
|
|
72
|
+
|
|
73
|
+
let tunnelStarted = false;
|
|
74
|
+
|
|
75
|
+
devServer.stdout.on('data', async (data) => {
|
|
76
|
+
const output = data.toString();
|
|
77
|
+
if (!tunnelStarted && (output.includes('Local:') || output.includes('Compiled successfully'))) {
|
|
78
|
+
tunnelStarted = true;
|
|
79
|
+
console.log(\`\\n[Fleetbo] 🔗 React is ready. Opening secure tunnel...\`);
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const url = await ngrok.connect({ addr: PORT });
|
|
83
|
+
await syncFirebase(keyApp, url);
|
|
84
|
+
} catch (err) {
|
|
85
|
+
console.error('\\n[Fleetbo] ❌ NGROK CONNECTION FAILED');
|
|
86
|
+
if (err.message.includes('ERR_NGROK_4018') || err.message.includes('limited') || err.message.includes('authtoken')) {
|
|
87
|
+
console.error('-------------------------------------------------------');
|
|
88
|
+
console.error('⚠️ MISSING OR INVALID AUTH TOKEN');
|
|
89
|
+
console.error(' Run this command once:');
|
|
90
|
+
console.error(' npx ngrok config add-authtoken <YOUR_TOKEN>');
|
|
91
|
+
console.error('-------------------------------------------------------');
|
|
92
|
+
}
|
|
93
|
+
cleanupAndExit(1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
runGuardian();
|
|
100
|
+
`;
|
|
101
|
+
|
|
18
102
|
// --- Analyse des Arguments ---
|
|
19
103
|
const args = process.argv.slice(2);
|
|
20
104
|
const projectNameArg = args.find(arg => !arg.startsWith('--'));
|
|
21
105
|
const tokenArg = args.find(arg => arg.startsWith('--token='));
|
|
106
|
+
const bootstrapTokenArg = tokenArg ? tokenArg.split('=')[1] : null;
|
|
22
107
|
|
|
23
|
-
if (!projectNameArg) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const bootstrapToken = tokenArg ? tokenArg.split('=')[1] : null;
|
|
30
|
-
|
|
31
|
-
if (!bootstrapToken) {
|
|
32
|
-
console.error('\n ❌ Error : The bootstrap token is missing.');
|
|
33
|
-
console.log(' Usage: npx create-fleetbo-project <project-name> --token=<your-token>');
|
|
34
|
-
process.exit(1);
|
|
108
|
+
if (!projectNameArg || !bootstrapTokenArg) {
|
|
109
|
+
console.error('\n ❌ Usage: npx create-fleetbo-project <project-name> --token=<your-token>');
|
|
110
|
+
process.exit(1);
|
|
35
111
|
}
|
|
36
112
|
|
|
37
113
|
const projectName = projectNameArg;
|
|
38
114
|
const projectDir = path.join(process.cwd(), projectName);
|
|
39
115
|
|
|
40
|
-
// --- Fonctions Utilitaires ---
|
|
41
116
|
|
|
117
|
+
// --- Fonctions Utilitaires ---
|
|
42
118
|
function fetchProjectKeys(token) {
|
|
43
119
|
return new Promise((resolve, reject) => {
|
|
44
120
|
const postData = JSON.stringify({ token });
|
|
@@ -48,16 +124,8 @@ function fetchProjectKeys(token) {
|
|
|
48
124
|
res.on('data', (chunk) => { data += chunk; });
|
|
49
125
|
res.on('end', () => {
|
|
50
126
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
51
|
-
try {
|
|
52
|
-
|
|
53
|
-
} catch (e) {
|
|
54
|
-
reject(new Error('Invalid response from the key server.'));
|
|
55
|
-
}
|
|
56
|
-
} else {
|
|
57
|
-
let errorMsg = `Server error (code: ${res.statusCode})`;
|
|
58
|
-
try { errorMsg = JSON.parse(data).error || errorMsg; } catch(e){}
|
|
59
|
-
reject(new Error(errorMsg));
|
|
60
|
-
}
|
|
127
|
+
try { resolve(JSON.parse(data)); } catch (e) { reject(new Error('Invalid response')); }
|
|
128
|
+
} else { reject(new Error(`Server error ${res.statusCode}`)); }
|
|
61
129
|
});
|
|
62
130
|
});
|
|
63
131
|
req.on('error', (e) => reject(e));
|
|
@@ -66,123 +134,76 @@ function fetchProjectKeys(token) {
|
|
|
66
134
|
});
|
|
67
135
|
}
|
|
68
136
|
|
|
69
|
-
// --- CORRECTION ICI : Gestion des redirections (301/302) ---
|
|
70
137
|
function downloadEngine(url, dest) {
|
|
71
138
|
return new Promise((resolve, reject) => {
|
|
72
139
|
const request = https.get(url, (response) => {
|
|
73
|
-
// 1. Gérer la redirection (GitHub renvoie souvent 302 Found)
|
|
74
140
|
if (response.statusCode === 301 || response.statusCode === 302) {
|
|
75
|
-
|
|
76
|
-
if (!newUrl) {
|
|
77
|
-
reject(new Error("Redirect status found but no location header."));
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
// Appel récursif avec la nouvelle URL
|
|
81
|
-
downloadEngine(newUrl, dest)
|
|
82
|
-
.then(resolve)
|
|
83
|
-
.catch(reject);
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// 2. Vérifier le succès (200 OK)
|
|
88
|
-
if (response.statusCode !== 200) {
|
|
89
|
-
reject(new Error(`Failed to download engine (Status: ${response.statusCode})`));
|
|
141
|
+
downloadEngine(response.headers.location, dest).then(resolve).catch(reject);
|
|
90
142
|
return;
|
|
91
143
|
}
|
|
92
|
-
|
|
93
|
-
// 3. Écriture du fichier
|
|
144
|
+
if (response.statusCode !== 200) { reject(new Error(`Status: ${response.statusCode}`)); return; }
|
|
94
145
|
const file = fs.createWriteStream(dest);
|
|
95
146
|
response.pipe(file);
|
|
96
|
-
|
|
97
|
-
file.on('
|
|
98
|
-
file.close(resolve);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
file.on('error', (err) => {
|
|
102
|
-
fs.unlink(dest, () => {}); // Supprimer fichier incomplet
|
|
103
|
-
reject(err);
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
request.on('error', (err) => {
|
|
108
|
-
fs.unlink(dest, () => {});
|
|
109
|
-
reject(err);
|
|
147
|
+
file.on('finish', () => file.close(resolve));
|
|
148
|
+
file.on('error', (err) => { fs.unlink(dest, () => {}); reject(err); });
|
|
110
149
|
});
|
|
150
|
+
request.on('error', (err) => reject(err));
|
|
111
151
|
});
|
|
112
152
|
}
|
|
113
153
|
|
|
114
154
|
// --- Fonction Principale ---
|
|
115
|
-
|
|
116
155
|
async function setupProject() {
|
|
117
156
|
console.log(`\n⚡ Initializing Fleetbo Framework for "${projectName}"...`);
|
|
118
157
|
|
|
119
158
|
try {
|
|
120
|
-
if (fs.existsSync(projectDir)) {
|
|
121
|
-
throw new Error(`Directory "${projectName}" already exists.`);
|
|
122
|
-
}
|
|
159
|
+
if (fs.existsSync(projectDir)) throw new Error(`Directory "${projectName}" already exists.`);
|
|
123
160
|
fs.mkdirSync(projectDir);
|
|
124
161
|
|
|
125
|
-
//
|
|
162
|
+
// 1. Download
|
|
126
163
|
console.log(' [1/6] 📥 Downloading Fleetbo Core Engine...');
|
|
127
164
|
const archivePath = path.join(projectDir, 'engine.tar.gz');
|
|
128
|
-
|
|
129
|
-
// Cette fonction gère maintenant la redirection GitHub
|
|
130
165
|
await downloadEngine(archiveUrl, archivePath);
|
|
131
166
|
|
|
132
|
-
//
|
|
167
|
+
// 2. Extract
|
|
133
168
|
console.log(' [2/6] 📦 Scaffolding project structure...');
|
|
134
169
|
try {
|
|
135
|
-
// On ignore les erreurs de tar si c'est juste des warnings, mais on catch les erreurs fatales
|
|
136
170
|
execSync(`tar -xf "${archivePath}" -C "${projectDir}" --strip-components=1`, { stdio: 'ignore' });
|
|
137
171
|
fs.unlinkSync(archivePath);
|
|
138
|
-
} catch (e) {
|
|
139
|
-
// Fallback pour Windows si 'tar' n'est pas dans le path (rare sur Win10/11 mais possible)
|
|
140
|
-
throw new Error("Failed to extract engine. Make sure 'tar' command is available.");
|
|
141
|
-
}
|
|
172
|
+
} catch (e) { throw new Error("Failed to extract engine. Ensure 'tar' is available."); }
|
|
142
173
|
|
|
143
174
|
process.chdir(projectDir);
|
|
144
175
|
|
|
145
|
-
//
|
|
176
|
+
// 3. Auth
|
|
146
177
|
console.log(' [3/6] 🔑 Authenticating with Fleetbo Cloud...');
|
|
147
|
-
const keys = await fetchProjectKeys(
|
|
148
|
-
if (!keys.enterpriseId
|
|
149
|
-
throw new Error("Received keys from the server are invalid.");
|
|
150
|
-
}
|
|
178
|
+
const keys = await fetchProjectKeys(bootstrapTokenArg);
|
|
179
|
+
if (!keys.enterpriseId) throw new Error("Invalid keys.");
|
|
151
180
|
|
|
152
|
-
//
|
|
153
|
-
console.log(' [4/6] ⚙️ Configuring environment...');
|
|
181
|
+
// 4. Environment & CLI Generation
|
|
182
|
+
console.log(' [4/6] ⚙️ Configuring environment & CLI...');
|
|
154
183
|
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`;
|
|
155
184
|
fs.writeFileSync(path.join(projectDir, '.env'), envContent, 'utf8');
|
|
185
|
+
fs.writeFileSync(path.join(projectDir, 'cli.js'), CLI_SCRIPT_CONTENT, 'utf8');
|
|
156
186
|
|
|
157
|
-
//
|
|
187
|
+
// 5. Deps
|
|
158
188
|
console.log(' [5/6] 📚 Installing dependencies...');
|
|
159
|
-
// On installe les deps du projet + ngrok en dev
|
|
160
189
|
execSync('npm install', { stdio: 'inherit' });
|
|
161
|
-
|
|
190
|
+
// AJOUT DE AXIOS ICI
|
|
191
|
+
execSync('npm install ngrok dotenv axios --save-dev', { stdio: 'ignore' });
|
|
162
192
|
|
|
163
|
-
//
|
|
193
|
+
// 6. Finalize
|
|
164
194
|
console.log(' [6/6] ✨ Finalizing setup...');
|
|
165
195
|
const packageJsonPath = path.join(projectDir, 'package.json');
|
|
166
196
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
167
197
|
packageJson.name = projectName;
|
|
168
|
-
|
|
169
|
-
packageJson.scripts = {
|
|
170
|
-
...packageJson.scripts,
|
|
171
|
-
"fleetbo": "node cli.js",
|
|
172
|
-
"dev": "node cli.js"
|
|
173
|
-
};
|
|
198
|
+
packageJson.scripts = { ...packageJson.scripts, "fleetbo": "node cli.js", "dev": "node cli.js" };
|
|
174
199
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf8');
|
|
175
200
|
|
|
176
201
|
console.log('\n✅ Project successfully created!');
|
|
177
|
-
console.log(`\n👉
|
|
178
|
-
console.log(` cd ${projectName}`);
|
|
179
|
-
console.log(` npm run fleetbo`);
|
|
202
|
+
console.log(`\n👉 Run: cd ${projectName} && npm run fleetbo`);
|
|
180
203
|
|
|
181
204
|
} catch (error) {
|
|
182
205
|
console.error('\n❌ Setup failed:', error.message);
|
|
183
|
-
if (fs.existsSync(projectDir)) {
|
|
184
|
-
try { fs.rmSync(projectDir, { recursive: true, force: true }); } catch(e){}
|
|
185
|
-
}
|
|
206
|
+
if (fs.existsSync(projectDir)) try { fs.rmSync(projectDir, { recursive: true, force: true }); } catch(e){}
|
|
186
207
|
process.exit(1);
|
|
187
208
|
}
|
|
188
209
|
}
|