create-fleetbo-project 1.2.9 → 1.2.11
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 +124 -86
- package/package.json +1 -1
|
@@ -4,44 +4,119 @@ 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');
|
|
8
7
|
|
|
9
8
|
// --- Configuration ---
|
|
10
|
-
// On cible l'archive (zip/tar) plutôt que le repo git
|
|
11
9
|
const repoOwner = 'FleetFleetbo';
|
|
12
10
|
const repoName = 'dev.fleetbo.io';
|
|
13
11
|
const branchName = 'master';
|
|
14
|
-
|
|
15
|
-
// URL de téléchargement direct de l'archive (plus rapide, pas de .git)
|
|
16
12
|
const archiveUrl = `https://github.com/${repoOwner}/${repoName}/archive/refs/heads/${branchName}.tar.gz`;
|
|
17
13
|
const bootstrapUrl = 'https://us-central1-myapp-259bf.cloudfunctions.net/bootstrapProject';
|
|
18
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
|
+
|
|
19
102
|
// --- Analyse des Arguments ---
|
|
20
103
|
const args = process.argv.slice(2);
|
|
21
104
|
const projectNameArg = args.find(arg => !arg.startsWith('--'));
|
|
22
105
|
const tokenArg = args.find(arg => arg.startsWith('--token='));
|
|
23
106
|
|
|
24
|
-
if (!projectNameArg) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const bootstrapToken = tokenArg ? tokenArg.split('=')[1] : null;
|
|
31
|
-
|
|
32
|
-
if (!bootstrapToken) {
|
|
33
|
-
console.error('\n ❌ Error : The bootstrap token is missing.');
|
|
34
|
-
console.log(' Usage: npx create-fleetbo-project <project-name> --token=<your-token>');
|
|
35
|
-
process.exit(1);
|
|
107
|
+
if (!projectNameArg || !bootstrapToken(tokenArg)) {
|
|
108
|
+
console.error('\n ❌ Usage: npx create-fleetbo-project <project-name> --token=<your-token>');
|
|
109
|
+
process.exit(1);
|
|
36
110
|
}
|
|
37
111
|
|
|
112
|
+
function bootstrapToken(arg) { return arg ? arg.split('=')[1] : null; }
|
|
38
113
|
const projectName = projectNameArg;
|
|
39
114
|
const projectDir = path.join(process.cwd(), projectName);
|
|
115
|
+
const bToken = bootstrapToken(tokenArg);
|
|
40
116
|
|
|
41
|
-
// --- Fonctions Utilitaires ---
|
|
42
117
|
|
|
118
|
+
// --- Fonctions Utilitaires ---
|
|
43
119
|
function fetchProjectKeys(token) {
|
|
44
|
-
// ... (Code inchangé pour la récupération des clés) ...
|
|
45
120
|
return new Promise((resolve, reject) => {
|
|
46
121
|
const postData = JSON.stringify({ token });
|
|
47
122
|
const options = { method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) } };
|
|
@@ -50,15 +125,8 @@ function fetchProjectKeys(token) {
|
|
|
50
125
|
res.on('data', (chunk) => { data += chunk; });
|
|
51
126
|
res.on('end', () => {
|
|
52
127
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
53
|
-
try {
|
|
54
|
-
|
|
55
|
-
} catch (e) {
|
|
56
|
-
reject(new Error('Invalid response from the key server.'));
|
|
57
|
-
}
|
|
58
|
-
} else {
|
|
59
|
-
const errorMsg = JSON.parse(data).error || `Server error (code: ${res.statusCode})`;
|
|
60
|
-
reject(new Error(errorMsg));
|
|
61
|
-
}
|
|
128
|
+
try { resolve(JSON.parse(data)); } catch (e) { reject(new Error('Invalid response')); }
|
|
129
|
+
} else { reject(new Error(`Server error ${res.statusCode}`)); }
|
|
62
130
|
});
|
|
63
131
|
});
|
|
64
132
|
req.on('error', (e) => reject(e));
|
|
@@ -67,105 +135,78 @@ function fetchProjectKeys(token) {
|
|
|
67
135
|
});
|
|
68
136
|
}
|
|
69
137
|
|
|
70
|
-
// Fonction pour télécharger l'archive
|
|
71
138
|
function downloadEngine(url, dest) {
|
|
72
139
|
return new Promise((resolve, reject) => {
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
reject(new Error(`Failed to download engine (Status: ${response.statusCode})`));
|
|
140
|
+
const request = https.get(url, (response) => {
|
|
141
|
+
if (response.statusCode === 301 || response.statusCode === 302) {
|
|
142
|
+
downloadEngine(response.headers.location, dest).then(resolve).catch(reject);
|
|
77
143
|
return;
|
|
78
144
|
}
|
|
145
|
+
if (response.statusCode !== 200) { reject(new Error(`Status: ${response.statusCode}`)); return; }
|
|
146
|
+
const file = fs.createWriteStream(dest);
|
|
79
147
|
response.pipe(file);
|
|
80
|
-
file.on('finish', () =>
|
|
81
|
-
|
|
82
|
-
});
|
|
83
|
-
}).on('error', (err) => {
|
|
84
|
-
fs.unlink(dest, () => {}); // Supprimer le fichier partiel
|
|
85
|
-
reject(err);
|
|
148
|
+
file.on('finish', () => file.close(resolve));
|
|
149
|
+
file.on('error', (err) => { fs.unlink(dest, () => {}); reject(err); });
|
|
86
150
|
});
|
|
151
|
+
request.on('error', (err) => reject(err));
|
|
87
152
|
});
|
|
88
153
|
}
|
|
89
154
|
|
|
90
155
|
// --- Fonction Principale ---
|
|
91
|
-
|
|
92
156
|
async function setupProject() {
|
|
93
157
|
console.log(`\n⚡ Initializing Fleetbo Framework for "${projectName}"...`);
|
|
94
158
|
|
|
95
159
|
try {
|
|
96
|
-
|
|
97
|
-
if (fs.existsSync(projectDir)) {
|
|
98
|
-
throw new Error(`Directory "${projectName}" already exists.`);
|
|
99
|
-
}
|
|
160
|
+
if (fs.existsSync(projectDir)) throw new Error(`Directory "${projectName}" already exists.`);
|
|
100
161
|
fs.mkdirSync(projectDir);
|
|
101
162
|
|
|
102
|
-
//
|
|
163
|
+
// 1. Download
|
|
103
164
|
console.log(' [1/6] 📥 Downloading Fleetbo Core Engine...');
|
|
104
165
|
const archivePath = path.join(projectDir, 'engine.tar.gz');
|
|
105
166
|
await downloadEngine(archiveUrl, archivePath);
|
|
106
167
|
|
|
107
|
-
//
|
|
168
|
+
// 2. Extract
|
|
108
169
|
console.log(' [2/6] 📦 Scaffolding project structure...');
|
|
109
|
-
// On utilise 'tar' qui est présent sur Win10+, Mac et Linux.
|
|
110
|
-
// --strip-components=1 permet de retirer le dossier racine "dev.fleetbo.io-master" de l'archive
|
|
111
170
|
try {
|
|
112
|
-
execSync(`tar -xf "${archivePath}" -C "${projectDir}" --strip-components=1
|
|
113
|
-
fs.unlinkSync(archivePath);
|
|
114
|
-
} catch (e) {
|
|
115
|
-
throw new Error("Failed to extract the engine. Ensure 'tar' is available on your system.");
|
|
116
|
-
}
|
|
171
|
+
execSync(`tar -xf "${archivePath}" -C "${projectDir}" --strip-components=1`, { stdio: 'ignore' });
|
|
172
|
+
fs.unlinkSync(archivePath);
|
|
173
|
+
} catch (e) { throw new Error("Failed to extract engine. Ensure 'tar' is available."); }
|
|
117
174
|
|
|
118
|
-
// On se déplace dans le dossier (Plus besoin de supprimer .git car il n'y en a pas !)
|
|
119
175
|
process.chdir(projectDir);
|
|
120
176
|
|
|
121
|
-
//
|
|
177
|
+
// 3. Auth
|
|
122
178
|
console.log(' [3/6] 🔑 Authenticating with Fleetbo Cloud...');
|
|
123
|
-
const keys = await fetchProjectKeys(
|
|
124
|
-
if (!keys.enterpriseId
|
|
125
|
-
throw new Error("Received keys from the server are invalid.");
|
|
126
|
-
}
|
|
179
|
+
const keys = await fetchProjectKeys(bToken);
|
|
180
|
+
if (!keys.enterpriseId) throw new Error("Invalid keys.");
|
|
127
181
|
|
|
128
|
-
//
|
|
129
|
-
console.log(' [4/6] ⚙️ Configuring environment...');
|
|
130
|
-
// Configuration .env optimisée pour le simulateur (DANGEROUSLY_DISABLE_HOST_CHECK)
|
|
182
|
+
// 4. Environment & CLI Generation
|
|
183
|
+
console.log(' [4/6] ⚙️ Configuring environment & CLI...');
|
|
131
184
|
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`;
|
|
132
185
|
fs.writeFileSync(path.join(projectDir, '.env'), envContent, 'utf8');
|
|
133
186
|
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
//
|
|
187
|
+
// --- ICI ON CRÉE LE FICHIER CLI.JS ---
|
|
188
|
+
fs.writeFileSync(path.join(projectDir, 'cli.js'), CLI_SCRIPT_CONTENT, 'utf8');
|
|
189
|
+
// -------------------------------------
|
|
190
|
+
|
|
191
|
+
// 5. Deps
|
|
192
|
+
console.log(' [5/6] 📚 Installing dependencies...');
|
|
137
193
|
execSync('npm install', { stdio: 'inherit' });
|
|
138
|
-
|
|
139
|
-
// Installation des outils de dev (Ngrok est vital pour le flow)
|
|
140
|
-
console.log(' Installing development tools...');
|
|
141
194
|
execSync('npm install ngrok dotenv --save-dev', { stdio: 'ignore' });
|
|
142
195
|
|
|
143
|
-
//
|
|
196
|
+
// 6. Finalize
|
|
144
197
|
console.log(' [6/6] ✨ Finalizing setup...');
|
|
145
|
-
|
|
146
198
|
const packageJsonPath = path.join(projectDir, 'package.json');
|
|
147
199
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
148
200
|
packageJson.name = projectName;
|
|
149
|
-
|
|
150
|
-
// On ajoute la commande 'fleetbo'
|
|
151
|
-
packageJson.scripts = {
|
|
152
|
-
...packageJson.scripts,
|
|
153
|
-
"fleetbo": "node cli.js",
|
|
154
|
-
"dev": "node cli.js"
|
|
155
|
-
};
|
|
201
|
+
packageJson.scripts = { ...packageJson.scripts, "fleetbo": "node cli.js", "dev": "node cli.js" };
|
|
156
202
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf8');
|
|
157
203
|
|
|
158
204
|
console.log('\n✅ Project successfully created!');
|
|
159
|
-
console.log(`\n👉
|
|
160
|
-
console.log(` cd ${projectName}`);
|
|
161
|
-
console.log(` npm run fleetbo`);
|
|
205
|
+
console.log(`\n👉 Run: cd ${projectName} && npm run fleetbo`);
|
|
162
206
|
|
|
163
207
|
} catch (error) {
|
|
164
208
|
console.error('\n❌ Setup failed:', error.message);
|
|
165
|
-
|
|
166
|
-
if (fs.existsSync(projectDir)) {
|
|
167
|
-
try { fs.rmSync(projectDir, { recursive: true, force: true }); } catch(e){}
|
|
168
|
-
}
|
|
209
|
+
if (fs.existsSync(projectDir)) try { fs.rmSync(projectDir, { recursive: true, force: true }); } catch(e){}
|
|
169
210
|
process.exit(1);
|
|
170
211
|
}
|
|
171
212
|
}
|
|
@@ -174,9 +215,6 @@ setupProject();
|
|
|
174
215
|
|
|
175
216
|
|
|
176
217
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
218
|
{/*
|
|
181
219
|
|
|
182
220
|
const { execSync } = require('child_process');
|