Files
wifi-densepose/vendor/ruvector/examples/edge-net/pkg/firebase-setup.js

436 lines
14 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* @ruvector/edge-net Firebase Setup
*
* Secure setup using Google Cloud CLI and Application Default Credentials.
* No API keys stored in environment variables - uses gcloud auth instead.
*
* Prerequisites:
* 1. Install Google Cloud CLI: https://cloud.google.com/sdk/docs/install
* 2. Login: gcloud auth login
* 3. Login for application: gcloud auth application-default login
*
* Usage:
* npx edge-net firebase-setup
* npx edge-net firebase-setup --project my-project-id
* npx edge-net firebase-setup --check
*
* @module @ruvector/edge-net/firebase-setup
*/
import { execSync, spawn } from 'child_process';
import { existsSync, writeFileSync, readFileSync, mkdirSync } from 'fs';
import { homedir } from 'os';
import { join } from 'path';
// ============================================
// CONFIGURATION
// ============================================
const CONFIG_DIR = join(homedir(), '.edge-net');
const CONFIG_FILE = join(CONFIG_DIR, 'firebase.json');
// Required Firebase services
const REQUIRED_APIS = [
'firebase.googleapis.com',
'firestore.googleapis.com',
'firebasedatabase.googleapis.com',
];
// ============================================
// GCLOUD HELPERS
// ============================================
/**
* Check if gcloud CLI is installed
*/
function checkGcloud() {
try {
execSync('gcloud --version', { stdio: 'pipe' });
return true;
} catch {
return false;
}
}
/**
* Get current gcloud configuration
*/
function getGcloudConfig() {
try {
const account = execSync('gcloud config get-value account', { stdio: 'pipe' }).toString().trim();
const project = execSync('gcloud config get-value project', { stdio: 'pipe' }).toString().trim();
return { account, project };
} catch {
return { account: null, project: null };
}
}
/**
* Check Application Default Credentials
*/
function checkADC() {
const adcPath = join(homedir(), '.config', 'gcloud', 'application_default_credentials.json');
return existsSync(adcPath);
}
/**
* Enable required APIs
*/
function enableAPIs(projectId) {
console.log('\n📦 Enabling required Firebase APIs...');
for (const api of REQUIRED_APIS) {
try {
execSync(`gcloud services enable ${api} --project=${projectId}`, { stdio: 'pipe' });
console.log(`${api}`);
} catch (err) {
console.log(` ⚠️ ${api} (may already be enabled)`);
}
}
}
/**
* Create Firestore database
*/
function createFirestore(projectId) {
console.log('\n🔥 Setting up Firestore...');
try {
// Check if Firestore already exists
execSync(`gcloud firestore databases describe --project=${projectId}`, { stdio: 'pipe' });
console.log(' ✅ Firestore database exists');
} catch {
// Create Firestore in native mode
try {
execSync(`gcloud firestore databases create --location=us-central --project=${projectId}`, { stdio: 'pipe' });
console.log(' ✅ Firestore database created (us-central)');
} catch (err) {
console.log(' ⚠️ Could not create Firestore (may need manual setup)');
}
}
}
/**
* Create Realtime Database
*/
function createRealtimeDB(projectId) {
console.log('\n📊 Setting up Realtime Database...');
try {
execSync(`firebase database:instances:create ${projectId}-rtdb --project=${projectId} --location=us-central1`, { stdio: 'pipe' });
console.log(` ✅ Realtime Database created: ${projectId}-rtdb`);
} catch {
console.log(' ⚠️ Realtime Database (may need Firebase CLI or manual setup)');
console.log(' 💡 Run: npm install -g firebase-tools && firebase init database');
}
}
/**
* Setup Firestore security rules
*/
function setupSecurityRules(projectId) {
const rules = `rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Edge-net signaling - authenticated users can read/write their signals
match /edge-net/signals/{signalId} {
allow read: if request.auth != null && resource.data.to == request.auth.uid;
allow create: if request.auth != null && request.resource.data.from == request.auth.uid;
allow delete: if request.auth != null && resource.data.to == request.auth.uid;
}
// Edge-net peers - public read, authenticated write
match /edge-net/peers/{peerId} {
allow read: if true;
allow write: if request.auth != null && request.auth.uid == peerId;
}
// Edge-net ledger - user can only access own ledger
match /edge-net/ledger/{peerId} {
allow read, write: if request.auth != null && request.auth.uid == peerId;
}
}
}`;
console.log('\n🔒 Firestore Security Rules:');
console.log(' Store these in firestore.rules and deploy with:');
console.log(' firebase deploy --only firestore:rules\n');
console.log(rules);
// Save rules file
const rulesPath = join(process.cwd(), 'firestore.rules');
writeFileSync(rulesPath, rules);
console.log(`\n ✅ Saved to: ${rulesPath}`);
}
/**
* Setup Realtime Database security rules
*/
function setupRTDBRules(projectId) {
const rules = {
"rules": {
"presence": {
"$room": {
"$peerId": {
".read": true,
".write": "auth != null && auth.uid == $peerId"
}
}
}
}
};
console.log('\n🔒 Realtime Database Rules:');
console.log(JSON.stringify(rules, null, 2));
// Save rules file
const rulesPath = join(process.cwd(), 'database.rules.json');
writeFileSync(rulesPath, JSON.stringify(rules, null, 2));
console.log(`\n ✅ Saved to: ${rulesPath}`);
}
/**
* Generate local config (no secrets!)
*/
function generateConfig(projectId) {
const config = {
projectId,
// These are NOT secrets - they're meant to be public
// API key restrictions happen in Google Cloud Console
authDomain: `${projectId}.firebaseapp.com`,
databaseURL: `https://${projectId}-default-rtdb.firebaseio.com`,
storageBucket: `${projectId}.appspot.com`,
// Security note
_note: 'Use Application Default Credentials for server-side. Generate restricted API key for browser in Google Cloud Console.',
_adcCommand: 'gcloud auth application-default login',
};
// Create config directory
if (!existsSync(CONFIG_DIR)) {
mkdirSync(CONFIG_DIR, { recursive: true });
}
// Save config
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
console.log(`\n📁 Config saved to: ${CONFIG_FILE}`);
return config;
}
/**
* Get API key securely (creates if needed)
*/
async function setupAPIKey(projectId) {
console.log('\n🔑 API Key Setup:');
console.log(' For browser-side Firebase, you need a restricted API key.');
console.log(' \n Steps:');
console.log(' 1. Go to: https://console.cloud.google.com/apis/credentials?project=' + projectId);
console.log(' 2. Create API Key → Restrict to:');
console.log(' - HTTP referrers (websites): your-domain.com/*');
console.log(' - APIs: Firebase Realtime Database, Cloud Firestore');
console.log(' 3. Set environment variable: export FIREBASE_API_KEY=your-key');
console.log('\n For Node.js server-side, use Application Default Credentials (more secure):');
console.log(' gcloud auth application-default login');
}
// ============================================
// MAIN SETUP FLOW
// ============================================
async function setup(options = {}) {
console.log('🚀 Edge-Net Firebase Setup\n');
console.log('=' .repeat(50));
// Step 1: Check gcloud
console.log('\n1⃣ Checking Google Cloud CLI...');
if (!checkGcloud()) {
console.error('❌ Google Cloud CLI not found!');
console.log(' Install from: https://cloud.google.com/sdk/docs/install');
process.exit(1);
}
console.log(' ✅ gcloud CLI found');
// Step 2: Check authentication
console.log('\n2⃣ Checking authentication...');
const { account, project } = getGcloudConfig();
if (!account) {
console.error('❌ Not logged in to gcloud!');
console.log(' Run: gcloud auth login');
process.exit(1);
}
console.log(` ✅ Logged in as: ${account}`);
// Step 3: Check ADC
console.log('\n3⃣ Checking Application Default Credentials...');
if (!checkADC()) {
console.log(' ⚠️ ADC not configured');
console.log(' Run: gcloud auth application-default login');
console.log('\n Setting up now...');
try {
execSync('gcloud auth application-default login', { stdio: 'inherit' });
} catch {
console.log(' ⚠️ ADC setup cancelled or failed');
}
} else {
console.log(' ✅ ADC configured');
}
// Step 4: Select project
const projectId = options.project || project;
console.log(`\n4⃣ Using project: ${projectId}`);
if (!projectId) {
console.error('❌ No project specified!');
console.log(' Run: gcloud config set project YOUR_PROJECT_ID');
console.log(' Or: npx edge-net firebase-setup --project YOUR_PROJECT_ID');
process.exit(1);
}
// Step 5: Enable APIs
enableAPIs(projectId);
// Step 6: Setup Firestore
createFirestore(projectId);
// Step 7: Setup Realtime Database
createRealtimeDB(projectId);
// Step 8: Generate security rules
setupSecurityRules(projectId);
setupRTDBRules(projectId);
// Step 9: Generate config
const config = generateConfig(projectId);
// Step 10: API Key guidance
await setupAPIKey(projectId);
// Done!
console.log('\n' + '='.repeat(50));
console.log('✅ Firebase setup complete!\n');
console.log('Next steps:');
console.log('1. Deploy security rules: firebase deploy --only firestore:rules,database');
console.log('2. Create restricted API key in Google Cloud Console');
console.log('3. Set FIREBASE_API_KEY environment variable');
console.log('4. Test with: npx edge-net join\n');
return config;
}
/**
* Check current status
*/
function checkStatus() {
console.log('🔍 Edge-Net Firebase Status\n');
// Check gcloud
const hasGcloud = checkGcloud();
console.log(`gcloud CLI: ${hasGcloud ? '✅' : '❌'}`);
// Check auth
const { account, project } = getGcloudConfig();
console.log(`Logged in: ${account ? `${account}` : '❌'}`);
console.log(`Project: ${project ? `${project}` : '❌'}`);
// Check ADC
const hasADC = checkADC();
console.log(`Application Default Credentials: ${hasADC ? '✅' : '❌'}`);
// Check config file
const hasConfig = existsSync(CONFIG_FILE);
console.log(`Config file: ${hasConfig ? `${CONFIG_FILE}` : '❌'}`);
// Check env vars
const hasApiKey = !!process.env.FIREBASE_API_KEY;
console.log(`FIREBASE_API_KEY: ${hasApiKey ? '✅ (set)' : '⚠️ (not set - needed for browser)'}`);
console.log();
if (!hasGcloud || !account || !project) {
console.log('💡 Run setup: npx edge-net firebase-setup');
} else if (!hasADC) {
console.log('💡 Run: gcloud auth application-default login');
} else if (!hasConfig) {
console.log('💡 Run setup: npx edge-net firebase-setup');
} else {
console.log('✅ Ready to use Firebase bootstrap!');
}
}
/**
* Load saved config
*/
export function loadConfig() {
if (!existsSync(CONFIG_FILE)) {
return null;
}
try {
return JSON.parse(readFileSync(CONFIG_FILE, 'utf8'));
} catch {
return null;
}
}
/**
* Get Firebase config (from env vars or saved config)
*/
export function getFirebaseConfigSecure() {
// First try environment variables
const apiKey = process.env.FIREBASE_API_KEY;
const projectId = process.env.FIREBASE_PROJECT_ID;
if (apiKey && projectId) {
return {
apiKey,
projectId,
authDomain: process.env.FIREBASE_AUTH_DOMAIN || `${projectId}.firebaseapp.com`,
databaseURL: process.env.FIREBASE_DATABASE_URL || `https://${projectId}-default-rtdb.firebaseio.com`,
storageBucket: process.env.FIREBASE_STORAGE_BUCKET || `${projectId}.appspot.com`,
};
}
// Try saved config (needs API key from env still for security)
const config = loadConfig();
if (config && apiKey) {
return {
apiKey,
...config,
};
}
return null;
}
// ============================================
// CLI
// ============================================
const args = process.argv.slice(2);
if (args.includes('--check')) {
checkStatus();
} else if (args.includes('--help') || args.includes('-h')) {
console.log(`
Edge-Net Firebase Setup
Usage:
npx edge-net firebase-setup Setup Firebase with gcloud
npx edge-net firebase-setup --project ID Use specific project
npx edge-net firebase-setup --check Check current status
Prerequisites:
1. Install gcloud: https://cloud.google.com/sdk/docs/install
2. Login: gcloud auth login
3. Set project: gcloud config set project YOUR_PROJECT_ID
Security:
- Uses Application Default Credentials (no stored secrets)
- API keys restricted by domain in Google Cloud Console
- Firestore rules protect user data
`);
} else {
const projectIndex = args.indexOf('--project');
const project = projectIndex >= 0 ? args[projectIndex + 1] : null;
setup({ project });
}
export { setup, checkStatus };