@ruvector/edge-net 0.2.1 → 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.
@@ -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 };