248 lines
7.0 KiB
TypeScript
248 lines
7.0 KiB
TypeScript
import { loadOrbitDBModules } from './helia-wrapper';
|
|
import { RealIPFSService } from './ipfs-setup';
|
|
import { OrbitDBInstance } from '../../../src/framework/services/OrbitDBService';
|
|
|
|
export class RealOrbitDBService implements OrbitDBInstance {
|
|
private orbitdb: any;
|
|
private ipfsService: RealIPFSService;
|
|
private nodeIndex: number;
|
|
private databases: Map<string, any> = new Map();
|
|
|
|
constructor(nodeIndex: number, ipfsService: RealIPFSService) {
|
|
this.nodeIndex = nodeIndex;
|
|
this.ipfsService = ipfsService;
|
|
}
|
|
|
|
async init(): Promise<any> {
|
|
console.log(`🌀 Initializing OrbitDB for node ${this.nodeIndex}...`);
|
|
|
|
try {
|
|
// Load OrbitDB ES modules dynamically
|
|
const { createOrbitDB } = await loadOrbitDBModules();
|
|
|
|
const ipfs = this.ipfsService.getHelia();
|
|
if (!ipfs) {
|
|
throw new Error('IPFS node must be initialized before OrbitDB');
|
|
}
|
|
|
|
// Create OrbitDB instance
|
|
this.orbitdb = await createOrbitDB({
|
|
ipfs,
|
|
id: `orbitdb-node-${this.nodeIndex}`,
|
|
directory: `./orbitdb-${this.nodeIndex}`, // Local directory for this node
|
|
});
|
|
|
|
console.log(`✅ OrbitDB initialized for node ${this.nodeIndex}`);
|
|
console.log(`📍 OrbitDB ID: ${this.orbitdb.id}`);
|
|
|
|
return this.orbitdb;
|
|
} catch (error) {
|
|
console.error(`❌ Failed to initialize OrbitDB for node ${this.nodeIndex}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async openDB(name: string, type: string): Promise<any> {
|
|
if (!this.orbitdb) {
|
|
throw new Error('OrbitDB not initialized');
|
|
}
|
|
|
|
const dbKey = `${name}-${type}`;
|
|
|
|
// Check if database is already open
|
|
if (this.databases.has(dbKey)) {
|
|
return this.databases.get(dbKey);
|
|
}
|
|
|
|
try {
|
|
console.log(`📂 Opening ${type} database '${name}' on node ${this.nodeIndex}...`);
|
|
|
|
let database;
|
|
|
|
switch (type.toLowerCase()) {
|
|
case 'documents':
|
|
case 'docstore':
|
|
database = await this.orbitdb.open(name, {
|
|
type: 'documents',
|
|
AccessController: 'orbitdb',
|
|
});
|
|
break;
|
|
|
|
case 'events':
|
|
case 'eventlog':
|
|
database = await this.orbitdb.open(name, {
|
|
type: 'events',
|
|
AccessController: 'orbitdb',
|
|
});
|
|
break;
|
|
|
|
case 'keyvalue':
|
|
case 'kvstore':
|
|
database = await this.orbitdb.open(name, {
|
|
type: 'keyvalue',
|
|
AccessController: 'orbitdb',
|
|
});
|
|
break;
|
|
|
|
default:
|
|
// Default to documents store
|
|
database = await this.orbitdb.open(name, {
|
|
type: 'documents',
|
|
AccessController: 'orbitdb',
|
|
});
|
|
}
|
|
|
|
this.databases.set(dbKey, database);
|
|
|
|
console.log(`✅ Database '${name}' opened on node ${this.nodeIndex}`);
|
|
console.log(`🔗 Database address: ${database.address}`);
|
|
|
|
return database;
|
|
} catch (error) {
|
|
console.error(`❌ Failed to open database '${name}' on node ${this.nodeIndex}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async stop(): Promise<void> {
|
|
console.log(`🛑 Stopping OrbitDB for node ${this.nodeIndex}...`);
|
|
|
|
try {
|
|
// Close all open databases
|
|
for (const [name, database] of this.databases) {
|
|
try {
|
|
await database.close();
|
|
console.log(`📂 Closed database '${name}' on node ${this.nodeIndex}`);
|
|
} catch (error) {
|
|
console.warn(`⚠️ Error closing database '${name}':`, error);
|
|
}
|
|
}
|
|
this.databases.clear();
|
|
|
|
// Stop OrbitDB
|
|
if (this.orbitdb) {
|
|
await this.orbitdb.stop();
|
|
console.log(`✅ OrbitDB stopped for node ${this.nodeIndex}`);
|
|
}
|
|
} catch (error) {
|
|
console.error(`❌ Error stopping OrbitDB for node ${this.nodeIndex}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
getOrbitDB(): any {
|
|
return this.orbitdb;
|
|
}
|
|
|
|
// Additional utility methods for testing
|
|
async waitForReplication(database: any, timeout: number = 30000): Promise<boolean> {
|
|
const startTime = Date.now();
|
|
|
|
return new Promise((resolve) => {
|
|
const checkReplication = () => {
|
|
if (Date.now() - startTime > timeout) {
|
|
resolve(false);
|
|
return;
|
|
}
|
|
|
|
// Check if database has received updates from other peers
|
|
const peers = database.peers || [];
|
|
if (peers.length > 0) {
|
|
resolve(true);
|
|
return;
|
|
}
|
|
|
|
setTimeout(checkReplication, 100);
|
|
};
|
|
|
|
checkReplication();
|
|
});
|
|
}
|
|
|
|
async getDatabaseInfo(name: string, type: string): Promise<any> {
|
|
const dbKey = `${name}-${type}`;
|
|
const database = this.databases.get(dbKey);
|
|
|
|
if (!database) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
address: database.address,
|
|
type: database.type,
|
|
peers: database.peers || [],
|
|
all: await database.all(),
|
|
meta: database.meta || {},
|
|
};
|
|
}
|
|
}
|
|
|
|
// Utility function to create OrbitDB network from IPFS network
|
|
export async function createOrbitDBNetwork(
|
|
ipfsNodes: RealIPFSService[],
|
|
): Promise<RealOrbitDBService[]> {
|
|
console.log(`🌀 Creating OrbitDB network with ${ipfsNodes.length} nodes...`);
|
|
|
|
const orbitdbNodes: RealOrbitDBService[] = [];
|
|
|
|
// Create OrbitDB instances for each IPFS node
|
|
for (let i = 0; i < ipfsNodes.length; i++) {
|
|
const orbitdbService = new RealOrbitDBService(i, ipfsNodes[i]);
|
|
await orbitdbService.init();
|
|
orbitdbNodes.push(orbitdbService);
|
|
}
|
|
|
|
console.log(`✅ OrbitDB network created with ${orbitdbNodes.length} nodes`);
|
|
return orbitdbNodes;
|
|
}
|
|
|
|
export async function shutdownOrbitDBNetwork(orbitdbNodes: RealOrbitDBService[]): Promise<void> {
|
|
console.log(`🛑 Shutting down OrbitDB network...`);
|
|
|
|
// Stop all OrbitDB nodes
|
|
await Promise.all(orbitdbNodes.map((node) => node.stop()));
|
|
|
|
console.log(`✅ OrbitDB network shutdown complete`);
|
|
}
|
|
|
|
// Test utilities for database operations
|
|
export async function testDatabaseReplication(
|
|
orbitdbNodes: RealOrbitDBService[],
|
|
dbName: string,
|
|
dbType: string = 'documents',
|
|
): Promise<boolean> {
|
|
console.log(`🔄 Testing database replication for '${dbName}'...`);
|
|
|
|
if (orbitdbNodes.length < 2) {
|
|
console.log(`⚠️ Need at least 2 nodes for replication test`);
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
// Open database on first node and add data
|
|
const db1 = await orbitdbNodes[0].openDB(dbName, dbType);
|
|
await db1.put({ _id: 'test-doc-1', content: 'Hello from node 0', timestamp: Date.now() });
|
|
|
|
// Open same database on second node
|
|
const db2 = await orbitdbNodes[1].openDB(dbName, dbType);
|
|
|
|
// Wait for replication
|
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
|
|
// Check if data replicated
|
|
const db2Data = await db2.all();
|
|
const hasReplicatedData = db2Data.some((doc: any) => doc._id === 'test-doc-1');
|
|
|
|
if (hasReplicatedData) {
|
|
console.log(`✅ Database replication successful for '${dbName}'`);
|
|
return true;
|
|
} else {
|
|
console.log(`❌ Database replication failed for '${dbName}'`);
|
|
return false;
|
|
}
|
|
} catch (error) {
|
|
console.error(`❌ Error testing database replication:`, error);
|
|
return false;
|
|
}
|
|
}
|