@ruvector/edge-net 0.3.0 → 0.4.0
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/cli.js +113 -0
- package/firebase-setup.js +435 -0
- package/firebase-signaling.js +788 -0
- package/p2p.js +129 -4
- package/package.json +28 -4
package/cli.js
CHANGED
|
@@ -110,6 +110,9 @@ function printHelp() {
|
|
|
110
110
|
${c('bold', 'COMMANDS:')}
|
|
111
111
|
${c('green', 'start')} Start an edge-net node in the terminal
|
|
112
112
|
${c('green', 'join')} Join network with public key (multi-contributor support)
|
|
113
|
+
${c('green', 'genesis')} Start a genesis/signaling server for P2P network
|
|
114
|
+
${c('green', 'firebase')} Setup Firebase (Google Cloud) for P2P bootstrap
|
|
115
|
+
${c('green', 'p2p')} Start full P2P network node
|
|
113
116
|
${c('green', 'benchmark')} Run performance benchmarks
|
|
114
117
|
${c('green', 'info')} Show package and WASM information
|
|
115
118
|
${c('green', 'demo')} Run interactive demonstration
|
|
@@ -123,6 +126,15 @@ ${c('bold', 'EXAMPLES:')}
|
|
|
123
126
|
${c('dim', '# Join with new identity (multi-contributor)')}
|
|
124
127
|
$ npx @ruvector/edge-net join --generate
|
|
125
128
|
|
|
129
|
+
${c('dim', '# Start genesis/signaling server (for local P2P)')}
|
|
130
|
+
$ npx @ruvector/edge-net genesis --port 8787
|
|
131
|
+
|
|
132
|
+
${c('dim', '# Setup Firebase for cloud-based P2P bootstrap')}
|
|
133
|
+
$ npx @ruvector/edge-net firebase --project YOUR_PROJECT_ID
|
|
134
|
+
|
|
135
|
+
${c('dim', '# Start full P2P network node')}
|
|
136
|
+
$ npx @ruvector/edge-net p2p
|
|
137
|
+
|
|
126
138
|
${c('dim', '# Run benchmarks')}
|
|
127
139
|
$ npx @ruvector/edge-net benchmark
|
|
128
140
|
|
|
@@ -422,6 +434,94 @@ async function runJoin() {
|
|
|
422
434
|
child.on('close', (code) => process.exit(code));
|
|
423
435
|
}
|
|
424
436
|
|
|
437
|
+
async function runGenesis() {
|
|
438
|
+
// Delegate to genesis.js
|
|
439
|
+
printBanner();
|
|
440
|
+
console.log(`${c('bold', 'Starting Genesis/Signaling Server...')}\n`);
|
|
441
|
+
|
|
442
|
+
const { spawn } = await import('child_process');
|
|
443
|
+
const args = process.argv.slice(3);
|
|
444
|
+
|
|
445
|
+
// Default to port 8787 if not specified
|
|
446
|
+
if (!args.includes('--port') && !args.includes('-p')) {
|
|
447
|
+
args.push('--port', '8787');
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const child = spawn('node', [join(__dirname, 'genesis.js'), ...args], {
|
|
451
|
+
stdio: 'inherit'
|
|
452
|
+
});
|
|
453
|
+
child.on('close', (code) => process.exit(code));
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async function runFirebaseSetup() {
|
|
457
|
+
// Delegate to firebase-setup.js
|
|
458
|
+
printBanner();
|
|
459
|
+
console.log(`${c('bold', 'Firebase Setup (Google Cloud)')}\n`);
|
|
460
|
+
|
|
461
|
+
const { spawn } = await import('child_process');
|
|
462
|
+
const args = process.argv.slice(3);
|
|
463
|
+
const child = spawn('node', [join(__dirname, 'firebase-setup.js'), ...args], {
|
|
464
|
+
stdio: 'inherit'
|
|
465
|
+
});
|
|
466
|
+
child.on('close', (code) => process.exit(code));
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
async function runP2P() {
|
|
470
|
+
printBanner();
|
|
471
|
+
console.log(`${c('bold', 'Starting P2P Network Node...')}\n`);
|
|
472
|
+
|
|
473
|
+
await setupPolyfills();
|
|
474
|
+
|
|
475
|
+
try {
|
|
476
|
+
const { createP2PNetwork } = await import('./p2p.js');
|
|
477
|
+
|
|
478
|
+
// Generate node ID
|
|
479
|
+
const nodeId = `node-${Math.random().toString(36).slice(2, 10)}`;
|
|
480
|
+
|
|
481
|
+
console.log(`${c('cyan', 'Node ID:')} ${nodeId}`);
|
|
482
|
+
console.log(`${c('cyan', 'Mode:')} Firebase bootstrap → DHT → Full P2P\n`);
|
|
483
|
+
|
|
484
|
+
console.log(`${c('dim', 'Initializing components...')}`);
|
|
485
|
+
|
|
486
|
+
const network = await createP2PNetwork(
|
|
487
|
+
{ nodeId },
|
|
488
|
+
{
|
|
489
|
+
// Use Firebase bootstrap if configured, fall back to local
|
|
490
|
+
bootstrapStrategy: process.env.FIREBASE_API_KEY ? 'firebase' : 'local',
|
|
491
|
+
}
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
console.log(`\n${c('bold', 'NETWORK STATUS:')}`);
|
|
495
|
+
const stats = network.getStats();
|
|
496
|
+
console.log(` ${c('cyan', 'Mode:')} ${c('green', stats.mode)}`);
|
|
497
|
+
console.log(` ${c('cyan', 'Bootstrap:')} ${stats.bootstrapMode}`);
|
|
498
|
+
console.log(` ${c('cyan', 'Peers:')} ${stats.peers}`);
|
|
499
|
+
console.log(` ${c('cyan', 'Firebase:')} ${stats.firebaseConnected ? c('green', 'Connected') : c('yellow', 'Not configured')}`);
|
|
500
|
+
console.log(` ${c('cyan', 'DHT Peers:')} ${stats.dhtPeers}`);
|
|
501
|
+
console.log(` ${c('cyan', 'Ledger Balance:')} ${stats.ledgerBalance}`);
|
|
502
|
+
|
|
503
|
+
console.log(`\n${c('dim', 'Press Ctrl+C to stop.')}`);
|
|
504
|
+
|
|
505
|
+
// Periodic status updates
|
|
506
|
+
setInterval(() => {
|
|
507
|
+
const s = network.getStats();
|
|
508
|
+
process.stdout.write(`\r${c('dim', `[${new Date().toLocaleTimeString()}]`)} Peers: ${s.peers} | Mode: ${s.mode} | Balance: ${s.ledgerBalance} `);
|
|
509
|
+
}, 5000);
|
|
510
|
+
|
|
511
|
+
process.on('SIGINT', async () => {
|
|
512
|
+
console.log(`\n\n${c('yellow', 'Stopping P2P network...')}`);
|
|
513
|
+
await network.stop();
|
|
514
|
+
console.log(`${c('green', '✓')} Network stopped.`);
|
|
515
|
+
process.exit(0);
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
} catch (err) {
|
|
519
|
+
console.error(`${c('red', '✗ Failed to start P2P network:')}\n`, err.message);
|
|
520
|
+
console.log(`\n${c('dim', 'Tip: Run')} ${c('cyan', 'npx @ruvector/edge-net firebase')} ${c('dim', 'to setup Firebase bootstrap.')}`);
|
|
521
|
+
process.exit(1);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
425
525
|
// Main
|
|
426
526
|
const command = process.argv[2] || 'help';
|
|
427
527
|
|
|
@@ -432,6 +532,19 @@ switch (command) {
|
|
|
432
532
|
case 'join':
|
|
433
533
|
runJoin();
|
|
434
534
|
break;
|
|
535
|
+
case 'genesis':
|
|
536
|
+
case 'signaling':
|
|
537
|
+
runGenesis();
|
|
538
|
+
break;
|
|
539
|
+
case 'firebase':
|
|
540
|
+
case 'firebase-setup':
|
|
541
|
+
case 'gcloud':
|
|
542
|
+
runFirebaseSetup();
|
|
543
|
+
break;
|
|
544
|
+
case 'p2p':
|
|
545
|
+
case 'network':
|
|
546
|
+
runP2P();
|
|
547
|
+
break;
|
|
435
548
|
case 'benchmark':
|
|
436
549
|
case 'bench':
|
|
437
550
|
runBenchmark();
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @ruvector/edge-net Firebase Setup
|
|
4
|
+
*
|
|
5
|
+
* Secure setup using Google Cloud CLI and Application Default Credentials.
|
|
6
|
+
* No API keys stored in environment variables - uses gcloud auth instead.
|
|
7
|
+
*
|
|
8
|
+
* Prerequisites:
|
|
9
|
+
* 1. Install Google Cloud CLI: https://cloud.google.com/sdk/docs/install
|
|
10
|
+
* 2. Login: gcloud auth login
|
|
11
|
+
* 3. Login for application: gcloud auth application-default login
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* npx edge-net firebase-setup
|
|
15
|
+
* npx edge-net firebase-setup --project my-project-id
|
|
16
|
+
* npx edge-net firebase-setup --check
|
|
17
|
+
*
|
|
18
|
+
* @module @ruvector/edge-net/firebase-setup
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { execSync, spawn } from 'child_process';
|
|
22
|
+
import { existsSync, writeFileSync, readFileSync, mkdirSync } from 'fs';
|
|
23
|
+
import { homedir } from 'os';
|
|
24
|
+
import { join } from 'path';
|
|
25
|
+
|
|
26
|
+
// ============================================
|
|
27
|
+
// CONFIGURATION
|
|
28
|
+
// ============================================
|
|
29
|
+
|
|
30
|
+
const CONFIG_DIR = join(homedir(), '.edge-net');
|
|
31
|
+
const CONFIG_FILE = join(CONFIG_DIR, 'firebase.json');
|
|
32
|
+
|
|
33
|
+
// Required Firebase services
|
|
34
|
+
const REQUIRED_APIS = [
|
|
35
|
+
'firebase.googleapis.com',
|
|
36
|
+
'firestore.googleapis.com',
|
|
37
|
+
'firebasedatabase.googleapis.com',
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
// ============================================
|
|
41
|
+
// GCLOUD HELPERS
|
|
42
|
+
// ============================================
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check if gcloud CLI is installed
|
|
46
|
+
*/
|
|
47
|
+
function checkGcloud() {
|
|
48
|
+
try {
|
|
49
|
+
execSync('gcloud --version', { stdio: 'pipe' });
|
|
50
|
+
return true;
|
|
51
|
+
} catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get current gcloud configuration
|
|
58
|
+
*/
|
|
59
|
+
function getGcloudConfig() {
|
|
60
|
+
try {
|
|
61
|
+
const account = execSync('gcloud config get-value account', { stdio: 'pipe' }).toString().trim();
|
|
62
|
+
const project = execSync('gcloud config get-value project', { stdio: 'pipe' }).toString().trim();
|
|
63
|
+
return { account, project };
|
|
64
|
+
} catch {
|
|
65
|
+
return { account: null, project: null };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Check Application Default Credentials
|
|
71
|
+
*/
|
|
72
|
+
function checkADC() {
|
|
73
|
+
const adcPath = join(homedir(), '.config', 'gcloud', 'application_default_credentials.json');
|
|
74
|
+
return existsSync(adcPath);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Enable required APIs
|
|
79
|
+
*/
|
|
80
|
+
function enableAPIs(projectId) {
|
|
81
|
+
console.log('\n📦 Enabling required Firebase APIs...');
|
|
82
|
+
for (const api of REQUIRED_APIS) {
|
|
83
|
+
try {
|
|
84
|
+
execSync(`gcloud services enable ${api} --project=${projectId}`, { stdio: 'pipe' });
|
|
85
|
+
console.log(` ✅ ${api}`);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
console.log(` ⚠️ ${api} (may already be enabled)`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Create Firestore database
|
|
94
|
+
*/
|
|
95
|
+
function createFirestore(projectId) {
|
|
96
|
+
console.log('\n🔥 Setting up Firestore...');
|
|
97
|
+
try {
|
|
98
|
+
// Check if Firestore already exists
|
|
99
|
+
execSync(`gcloud firestore databases describe --project=${projectId}`, { stdio: 'pipe' });
|
|
100
|
+
console.log(' ✅ Firestore database exists');
|
|
101
|
+
} catch {
|
|
102
|
+
// Create Firestore in native mode
|
|
103
|
+
try {
|
|
104
|
+
execSync(`gcloud firestore databases create --location=us-central --project=${projectId}`, { stdio: 'pipe' });
|
|
105
|
+
console.log(' ✅ Firestore database created (us-central)');
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.log(' ⚠️ Could not create Firestore (may need manual setup)');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Create Realtime Database
|
|
114
|
+
*/
|
|
115
|
+
function createRealtimeDB(projectId) {
|
|
116
|
+
console.log('\n📊 Setting up Realtime Database...');
|
|
117
|
+
try {
|
|
118
|
+
execSync(`firebase database:instances:create ${projectId}-rtdb --project=${projectId} --location=us-central1`, { stdio: 'pipe' });
|
|
119
|
+
console.log(` ✅ Realtime Database created: ${projectId}-rtdb`);
|
|
120
|
+
} catch {
|
|
121
|
+
console.log(' ⚠️ Realtime Database (may need Firebase CLI or manual setup)');
|
|
122
|
+
console.log(' 💡 Run: npm install -g firebase-tools && firebase init database');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Setup Firestore security rules
|
|
128
|
+
*/
|
|
129
|
+
function setupSecurityRules(projectId) {
|
|
130
|
+
const rules = `rules_version = '2';
|
|
131
|
+
service cloud.firestore {
|
|
132
|
+
match /databases/{database}/documents {
|
|
133
|
+
// Edge-net signaling - authenticated users can read/write their signals
|
|
134
|
+
match /edge-net/signals/{signalId} {
|
|
135
|
+
allow read: if request.auth != null && resource.data.to == request.auth.uid;
|
|
136
|
+
allow create: if request.auth != null && request.resource.data.from == request.auth.uid;
|
|
137
|
+
allow delete: if request.auth != null && resource.data.to == request.auth.uid;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Edge-net peers - public read, authenticated write
|
|
141
|
+
match /edge-net/peers/{peerId} {
|
|
142
|
+
allow read: if true;
|
|
143
|
+
allow write: if request.auth != null && request.auth.uid == peerId;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Edge-net ledger - user can only access own ledger
|
|
147
|
+
match /edge-net/ledger/{peerId} {
|
|
148
|
+
allow read, write: if request.auth != null && request.auth.uid == peerId;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}`;
|
|
152
|
+
|
|
153
|
+
console.log('\n🔒 Firestore Security Rules:');
|
|
154
|
+
console.log(' Store these in firestore.rules and deploy with:');
|
|
155
|
+
console.log(' firebase deploy --only firestore:rules\n');
|
|
156
|
+
console.log(rules);
|
|
157
|
+
|
|
158
|
+
// Save rules file
|
|
159
|
+
const rulesPath = join(process.cwd(), 'firestore.rules');
|
|
160
|
+
writeFileSync(rulesPath, rules);
|
|
161
|
+
console.log(`\n ✅ Saved to: ${rulesPath}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Setup Realtime Database security rules
|
|
166
|
+
*/
|
|
167
|
+
function setupRTDBRules(projectId) {
|
|
168
|
+
const rules = {
|
|
169
|
+
"rules": {
|
|
170
|
+
"presence": {
|
|
171
|
+
"$room": {
|
|
172
|
+
"$peerId": {
|
|
173
|
+
".read": true,
|
|
174
|
+
".write": "auth != null && auth.uid == $peerId"
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
console.log('\n🔒 Realtime Database Rules:');
|
|
182
|
+
console.log(JSON.stringify(rules, null, 2));
|
|
183
|
+
|
|
184
|
+
// Save rules file
|
|
185
|
+
const rulesPath = join(process.cwd(), 'database.rules.json');
|
|
186
|
+
writeFileSync(rulesPath, JSON.stringify(rules, null, 2));
|
|
187
|
+
console.log(`\n ✅ Saved to: ${rulesPath}`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Generate local config (no secrets!)
|
|
192
|
+
*/
|
|
193
|
+
function generateConfig(projectId) {
|
|
194
|
+
const config = {
|
|
195
|
+
projectId,
|
|
196
|
+
// These are NOT secrets - they're meant to be public
|
|
197
|
+
// API key restrictions happen in Google Cloud Console
|
|
198
|
+
authDomain: `${projectId}.firebaseapp.com`,
|
|
199
|
+
databaseURL: `https://${projectId}-default-rtdb.firebaseio.com`,
|
|
200
|
+
storageBucket: `${projectId}.appspot.com`,
|
|
201
|
+
// Security note
|
|
202
|
+
_note: 'Use Application Default Credentials for server-side. Generate restricted API key for browser in Google Cloud Console.',
|
|
203
|
+
_adcCommand: 'gcloud auth application-default login',
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// Create config directory
|
|
207
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
208
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Save config
|
|
212
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
213
|
+
console.log(`\n📁 Config saved to: ${CONFIG_FILE}`);
|
|
214
|
+
|
|
215
|
+
return config;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Get API key securely (creates if needed)
|
|
220
|
+
*/
|
|
221
|
+
async function setupAPIKey(projectId) {
|
|
222
|
+
console.log('\n🔑 API Key Setup:');
|
|
223
|
+
console.log(' For browser-side Firebase, you need a restricted API key.');
|
|
224
|
+
console.log(' \n Steps:');
|
|
225
|
+
console.log(' 1. Go to: https://console.cloud.google.com/apis/credentials?project=' + projectId);
|
|
226
|
+
console.log(' 2. Create API Key → Restrict to:');
|
|
227
|
+
console.log(' - HTTP referrers (websites): your-domain.com/*');
|
|
228
|
+
console.log(' - APIs: Firebase Realtime Database, Cloud Firestore');
|
|
229
|
+
console.log(' 3. Set environment variable: export FIREBASE_API_KEY=your-key');
|
|
230
|
+
console.log('\n For Node.js server-side, use Application Default Credentials (more secure):');
|
|
231
|
+
console.log(' gcloud auth application-default login');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ============================================
|
|
235
|
+
// MAIN SETUP FLOW
|
|
236
|
+
// ============================================
|
|
237
|
+
|
|
238
|
+
async function setup(options = {}) {
|
|
239
|
+
console.log('🚀 Edge-Net Firebase Setup\n');
|
|
240
|
+
console.log('=' .repeat(50));
|
|
241
|
+
|
|
242
|
+
// Step 1: Check gcloud
|
|
243
|
+
console.log('\n1️⃣ Checking Google Cloud CLI...');
|
|
244
|
+
if (!checkGcloud()) {
|
|
245
|
+
console.error('❌ Google Cloud CLI not found!');
|
|
246
|
+
console.log(' Install from: https://cloud.google.com/sdk/docs/install');
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
console.log(' ✅ gcloud CLI found');
|
|
250
|
+
|
|
251
|
+
// Step 2: Check authentication
|
|
252
|
+
console.log('\n2️⃣ Checking authentication...');
|
|
253
|
+
const { account, project } = getGcloudConfig();
|
|
254
|
+
if (!account) {
|
|
255
|
+
console.error('❌ Not logged in to gcloud!');
|
|
256
|
+
console.log(' Run: gcloud auth login');
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
259
|
+
console.log(` ✅ Logged in as: ${account}`);
|
|
260
|
+
|
|
261
|
+
// Step 3: Check ADC
|
|
262
|
+
console.log('\n3️⃣ Checking Application Default Credentials...');
|
|
263
|
+
if (!checkADC()) {
|
|
264
|
+
console.log(' ⚠️ ADC not configured');
|
|
265
|
+
console.log(' Run: gcloud auth application-default login');
|
|
266
|
+
console.log('\n Setting up now...');
|
|
267
|
+
try {
|
|
268
|
+
execSync('gcloud auth application-default login', { stdio: 'inherit' });
|
|
269
|
+
} catch {
|
|
270
|
+
console.log(' ⚠️ ADC setup cancelled or failed');
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
console.log(' ✅ ADC configured');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Step 4: Select project
|
|
277
|
+
const projectId = options.project || project;
|
|
278
|
+
console.log(`\n4️⃣ Using project: ${projectId}`);
|
|
279
|
+
if (!projectId) {
|
|
280
|
+
console.error('❌ No project specified!');
|
|
281
|
+
console.log(' Run: gcloud config set project YOUR_PROJECT_ID');
|
|
282
|
+
console.log(' Or: npx edge-net firebase-setup --project YOUR_PROJECT_ID');
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Step 5: Enable APIs
|
|
287
|
+
enableAPIs(projectId);
|
|
288
|
+
|
|
289
|
+
// Step 6: Setup Firestore
|
|
290
|
+
createFirestore(projectId);
|
|
291
|
+
|
|
292
|
+
// Step 7: Setup Realtime Database
|
|
293
|
+
createRealtimeDB(projectId);
|
|
294
|
+
|
|
295
|
+
// Step 8: Generate security rules
|
|
296
|
+
setupSecurityRules(projectId);
|
|
297
|
+
setupRTDBRules(projectId);
|
|
298
|
+
|
|
299
|
+
// Step 9: Generate config
|
|
300
|
+
const config = generateConfig(projectId);
|
|
301
|
+
|
|
302
|
+
// Step 10: API Key guidance
|
|
303
|
+
await setupAPIKey(projectId);
|
|
304
|
+
|
|
305
|
+
// Done!
|
|
306
|
+
console.log('\n' + '='.repeat(50));
|
|
307
|
+
console.log('✅ Firebase setup complete!\n');
|
|
308
|
+
console.log('Next steps:');
|
|
309
|
+
console.log('1. Deploy security rules: firebase deploy --only firestore:rules,database');
|
|
310
|
+
console.log('2. Create restricted API key in Google Cloud Console');
|
|
311
|
+
console.log('3. Set FIREBASE_API_KEY environment variable');
|
|
312
|
+
console.log('4. Test with: npx edge-net join\n');
|
|
313
|
+
|
|
314
|
+
return config;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Check current status
|
|
319
|
+
*/
|
|
320
|
+
function checkStatus() {
|
|
321
|
+
console.log('🔍 Edge-Net Firebase Status\n');
|
|
322
|
+
|
|
323
|
+
// Check gcloud
|
|
324
|
+
const hasGcloud = checkGcloud();
|
|
325
|
+
console.log(`gcloud CLI: ${hasGcloud ? '✅' : '❌'}`);
|
|
326
|
+
|
|
327
|
+
// Check auth
|
|
328
|
+
const { account, project } = getGcloudConfig();
|
|
329
|
+
console.log(`Logged in: ${account ? `✅ ${account}` : '❌'}`);
|
|
330
|
+
console.log(`Project: ${project ? `✅ ${project}` : '❌'}`);
|
|
331
|
+
|
|
332
|
+
// Check ADC
|
|
333
|
+
const hasADC = checkADC();
|
|
334
|
+
console.log(`Application Default Credentials: ${hasADC ? '✅' : '❌'}`);
|
|
335
|
+
|
|
336
|
+
// Check config file
|
|
337
|
+
const hasConfig = existsSync(CONFIG_FILE);
|
|
338
|
+
console.log(`Config file: ${hasConfig ? `✅ ${CONFIG_FILE}` : '❌'}`);
|
|
339
|
+
|
|
340
|
+
// Check env vars
|
|
341
|
+
const hasApiKey = !!process.env.FIREBASE_API_KEY;
|
|
342
|
+
console.log(`FIREBASE_API_KEY: ${hasApiKey ? '✅ (set)' : '⚠️ (not set - needed for browser)'}`);
|
|
343
|
+
|
|
344
|
+
console.log();
|
|
345
|
+
|
|
346
|
+
if (!hasGcloud || !account || !project) {
|
|
347
|
+
console.log('💡 Run setup: npx edge-net firebase-setup');
|
|
348
|
+
} else if (!hasADC) {
|
|
349
|
+
console.log('💡 Run: gcloud auth application-default login');
|
|
350
|
+
} else if (!hasConfig) {
|
|
351
|
+
console.log('💡 Run setup: npx edge-net firebase-setup');
|
|
352
|
+
} else {
|
|
353
|
+
console.log('✅ Ready to use Firebase bootstrap!');
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Load saved config
|
|
359
|
+
*/
|
|
360
|
+
export function loadConfig() {
|
|
361
|
+
if (!existsSync(CONFIG_FILE)) {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
try {
|
|
366
|
+
return JSON.parse(readFileSync(CONFIG_FILE, 'utf8'));
|
|
367
|
+
} catch {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Get Firebase config (from env vars or saved config)
|
|
374
|
+
*/
|
|
375
|
+
export function getFirebaseConfigSecure() {
|
|
376
|
+
// First try environment variables
|
|
377
|
+
const apiKey = process.env.FIREBASE_API_KEY;
|
|
378
|
+
const projectId = process.env.FIREBASE_PROJECT_ID;
|
|
379
|
+
|
|
380
|
+
if (apiKey && projectId) {
|
|
381
|
+
return {
|
|
382
|
+
apiKey,
|
|
383
|
+
projectId,
|
|
384
|
+
authDomain: process.env.FIREBASE_AUTH_DOMAIN || `${projectId}.firebaseapp.com`,
|
|
385
|
+
databaseURL: process.env.FIREBASE_DATABASE_URL || `https://${projectId}-default-rtdb.firebaseio.com`,
|
|
386
|
+
storageBucket: process.env.FIREBASE_STORAGE_BUCKET || `${projectId}.appspot.com`,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Try saved config (needs API key from env still for security)
|
|
391
|
+
const config = loadConfig();
|
|
392
|
+
if (config && apiKey) {
|
|
393
|
+
return {
|
|
394
|
+
apiKey,
|
|
395
|
+
...config,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// ============================================
|
|
403
|
+
// CLI
|
|
404
|
+
// ============================================
|
|
405
|
+
|
|
406
|
+
const args = process.argv.slice(2);
|
|
407
|
+
|
|
408
|
+
if (args.includes('--check')) {
|
|
409
|
+
checkStatus();
|
|
410
|
+
} else if (args.includes('--help') || args.includes('-h')) {
|
|
411
|
+
console.log(`
|
|
412
|
+
Edge-Net Firebase Setup
|
|
413
|
+
|
|
414
|
+
Usage:
|
|
415
|
+
npx edge-net firebase-setup Setup Firebase with gcloud
|
|
416
|
+
npx edge-net firebase-setup --project ID Use specific project
|
|
417
|
+
npx edge-net firebase-setup --check Check current status
|
|
418
|
+
|
|
419
|
+
Prerequisites:
|
|
420
|
+
1. Install gcloud: https://cloud.google.com/sdk/docs/install
|
|
421
|
+
2. Login: gcloud auth login
|
|
422
|
+
3. Set project: gcloud config set project YOUR_PROJECT_ID
|
|
423
|
+
|
|
424
|
+
Security:
|
|
425
|
+
- Uses Application Default Credentials (no stored secrets)
|
|
426
|
+
- API keys restricted by domain in Google Cloud Console
|
|
427
|
+
- Firestore rules protect user data
|
|
428
|
+
`);
|
|
429
|
+
} else {
|
|
430
|
+
const projectIndex = args.indexOf('--project');
|
|
431
|
+
const project = projectIndex >= 0 ? args[projectIndex + 1] : null;
|
|
432
|
+
setup({ project });
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export { setup, checkStatus };
|