Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
162a99c
added-multiaddrs
Nkovaturient Jan 29, 2025
7b7c596
connectPeerByID-initial-fix
Nkovaturient Feb 3, 2025
0b97a97
modified-kadDHT-with-peerRouting-method
Nkovaturient Feb 6, 2025
322e072
Merge branch 'main' into connect-via-peerId
Nkovaturient Feb 6, 2025
6aef49c
prettier-code-styling
Nkovaturient Feb 6, 2025
ae5b517
add-peer-connectivity&validity
Nkovaturient Feb 6, 2025
3c20c29
Merge remote-tracking branch 'origin/main' into connect-via-peerId
2color Feb 12, 2025
e82651d
chore: normalize linebreaks
2color Feb 12, 2025
99b9826
chore:improve-peerID-connection-logic
Nkovaturient Feb 18, 2025
2ff31ee
Merge branch 'libp2p:main' into connect-via-peerId
Nkovaturient Feb 18, 2025
3dc0459
Merge branch 'main' of https://github.com/Nkovaturient/universal-conn…
Nkovaturient Feb 18, 2025
a0d5c28
Merge branch 'connect-via-peerId' of https://github.com/Nkovaturient/…
Nkovaturient Feb 18, 2025
18a31fb
Merge branch 'main' into connect-via-peerId
Nkovaturient Feb 21, 2025
c459ded
Refactor: Unify multiaddr and PeerID connection in index.tsx & run fo…
Nkovaturient Feb 21, 2025
a2cda72
Merge branch 'main' of https://github.com/Nkovaturient/universal-conn…
Nkovaturient Feb 21, 2025
eb137d3
resolve-merge-conflicts
Nkovaturient Feb 21, 2025
b5a69c3
removed-setConn-ctx+apt-peer-list-style
Nkovaturient Feb 27, 2025
94b2ed6
remove-setConn-ctx+revert-peer-list-styling
Nkovaturient Feb 27, 2025
1acb6a4
Merge branch 'libp2p:main' into connect-via-peerId
Nkovaturient Mar 2, 2025
d8c5d07
afresh-dial-peerID-ability
Nkovaturient Mar 13, 2025
ddc386d
Merge branch 'main' of https://github.com/Nkovaturient/universal-conn…
Nkovaturient Mar 13, 2025
a0314d8
Merge branch 'connect-via-peerId' of https://github.com/Nkovaturient/…
Nkovaturient Mar 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 37 additions & 19 deletions js-peer/src/components/peer-list.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,43 @@
import { XCircleIcon } from '@heroicons/react/24/solid'
import type { PeerId, Connection } from '@libp2p/interface'
import { Badge } from './badge'
import type { PeerId } from '@libp2p/interface'
import { useCallback } from 'react'
import { useLibp2pContext } from '@/context/ctx'
import { Multiaddr } from '@multiformats/multiaddr'

interface PeerListProps {
connections: Connection[]
connections: Array<{
id: string
remotePeer: PeerId
remoteAddr: Multiaddr
status: string
timeline: {
open: number
}
}>
}

export default function PeerList({ connections }: PeerListProps) {
return (
<ul role="list" className="divide-y divide-gray-100">
{connections.map((connection) => (
<Peer key={connection.id} connection={connection} />
))}
</ul>
<div className="mt-4">
<ul className="divide-y divide-gray-100">
{connections.map((connection) => (
<Peer key={connection.id} connection={connection} />
))}
</ul>
</div>
)
}

interface PeerProps {
connection: Connection
connection: {
id: string
remotePeer: PeerId
remoteAddr: Multiaddr
status: string
timeline: {
open: number
}
}
}
function Peer({ connection }: PeerProps) {
const { libp2p } = useLibp2pContext()
Expand All @@ -40,26 +58,26 @@ function Peer({ connection }: PeerProps) {
}

return (
<li key={connection.id} className="flex justify-between gap-x-6 py-3">
<li key={connection.id} className="flex justify-between flex-wrap mx-2 py-2 gap-x-6 py-5">
<div className="flex min-w-0 gap-x-4">
<div className="mt-1 flex items-center gap-x-1.5">
<div className="flex-none rounded-full bg-emerald-500/20 p-1">
<div className="h-1.5 w-1.5 rounded-full bg-emerald-500" />
</div>
</div>
{/* <img className="h-12 w-12 flex-none rounded-full bg-gray-50" src={person.imageUrl} alt="" /> */}
<div className="min-w-0 flex-auto">
<p className="text-sm font-semibold leading-6 text-gray-900">
{connection.remotePeer.toString()}{' '}
{connection.remoteAddr.protoNames().includes('webrtc') ? <Badge color="indigo">P2P Browser</Badge> : null}
</p>
<p className="mt-1 truncate text-xs leading-5 text-gray-500">
{ipAddr} {connection.remoteAddr.protoNames().join(', ')}
<p className="text-sm font-semibold leading-6 text-gray-900">{connection.remotePeer.toString()}</p>
<p className="mt-1 truncate text-xs leading-5 text-gray-500">{connection.remoteAddr.toString()}</p>
<p className="mt-1 text-xs leading-5 text-gray-500">
Connected: {new Date(connection.timeline.open).toLocaleString()}
</p>
</div>
</div>

{/* <div className="flex gap-x-2 items-center "> */}
<div className="hidden shrink-0 sm:flex sm:flex-col sm:items-end">
<p className={`text-sm leading-6 ${connection.status === 'open' ? 'text-green-500' : 'text-gray-500'}`}>
{connection.status === 'open' ? '🟢 Connected' : '⚫ Closed'}
</p>
</div>
<div className="hidden sm:flex sm:flex-col sm:items-end">
<button
onClick={() => handleDisconnectPeer(connection.remotePeer)}
Expand Down
169 changes: 169 additions & 0 deletions js-peer/src/components/peer-maddr.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import { useLibp2pContext } from '@/context/ctx'
import { multiaddr } from '@multiformats/multiaddr'
import { useState } from 'react'
import Spinner from '@/components/spinner'
import { connectToMultiaddr } from '../lib/libp2p'
import type { PeerId } from '@libp2p/interface'

interface PeerMaddrListProps {
resolvedMultiaddrs: string[]
setResolvedMultiaddrs: (addrs: string[]) => void
setError: (error: string | null) => void
}

export default function PeerMaddrList({ resolvedMultiaddrs, setResolvedMultiaddrs, setError }: PeerMaddrListProps) {
if (resolvedMultiaddrs.length === 0) return null

return (
<div className="mt-6 w-full">
<h4 className="text-lg font-semibold text-gray-900 mb-3">Found {resolvedMultiaddrs.length} addresses:</h4>
<ul className="p-4 border rounded-lg bg-gray-50 shadow-sm space-y-3">
{resolvedMultiaddrs.map((addr, index) => (
<MaddrItem key={index} addr={addr} setResolvedMultiaddrs={setResolvedMultiaddrs} setError={setError} />
))}
</ul>
</div>
)
}

interface MaddrItemProps {
addr: string
setResolvedMultiaddrs: (addrs: string[]) => void
setError: (error: string | null) => void
}

function MaddrItem({ addr, setResolvedMultiaddrs, setError }: MaddrItemProps) {
const [loading, setLoading] = useState(false)
const { libp2p, connections, setConnections } = useLibp2pContext()

// Helper to check if address is external
const isExternalAddress = (addr: string) => {
return !addr.includes('127.0.0.1') && !addr.includes('localhost') && !addr.includes('::1')
}

// Helper to validate transport stack
const hasValidTransportStack = (addr: string) => {
const hasWebTransport = addr.includes('webtransport') && addr.includes('certhash')
const hasWebRTC = addr.includes('webrtc') && addr.includes('certhash')
const hasQuic = addr.includes('quic-v1')

return hasWebTransport || (hasWebRTC && hasQuic)
}

const handleConnect = async () => {
setLoading(true)
try {
const maddr = multiaddr(addr)
const peerId = maddr.getPeerId()

if (!peerId) {
throw new Error('No peer ID found in multiaddr')
}

if (!isExternalAddress(addr)) {
console.warn('⚠️ Attempting to connect to local address, this might fail:', addr)
}

if (!hasValidTransportStack(addr)) {
throw new Error('Invalid or incomplete transport protocol stack')
}
console.log(`🔌 Attempting to connect to ${addr}`)

// Ensure the multiaddr includes the peer ID
const fullAddr = addr.includes(`/p2p/${peerId}`) ? addr : `${addr}/p2p/${peerId}`
const fullMaddr = multiaddr(fullAddr)
// Attempt connection
await connectToMultiaddr(libp2p)(fullMaddr)
console.log('✅ Successfully connected via:', fullAddr)

if (connections && !connections.find((conn) => conn.remotePeer.toString() === peerId)) {
const newConnections = [...connections]
const peerConnections = libp2p.getConnections(peerId as unknown as PeerId)
if (peerConnections.length > 0) {
newConnections.push(peerConnections[0])
setConnections(newConnections)
}
}

setError('✅ Successfully connected to peer!')
setResolvedMultiaddrs([])
} catch (err: unknown) {
const errorMessage = err instanceof Error ? err.message : String(err)
console.error('❌ Connection failed:', errorMessage)
setError(`❌ Failed to connect: ${errorMessage}`)
} finally {
setLoading(false)
}
}

// Only show valid multiaddrs that can be used for connection
const isValidMultiaddr = () => {
try {
const maddr = multiaddr(addr)
return !!maddr.getPeerId() // Only show if it has a peer ID
} catch {
return false
}
}

// Only show promising connection candidates
const getConnectionPriority = () => {
if (!isValidMultiaddr()) return 0
let priority = 1
if (isExternalAddress(addr)) priority += 2
if (addr.includes('webtransport')) priority += 3
if (addr.includes('webrtc')) priority += 2
return priority
}

// Don't render if priority is 0 (invalid)
if (getConnectionPriority() === 0) {
return null
}

return (
<li className="flex justify-between gap-x-6 py-3">
<div className="flex min-w-0 gap-x-4">
<div className="min-w-0 flex-auto">
<p className="text-sm font-semibold leading-6 text-gray-900 break-all">
<span
className={`inline-block px-2 py-1 text-xs rounded-full mr-2
${
addr.includes('webtransport')
? 'bg-green-100 text-green-800'
: addr.includes('webrtc')
? 'bg-blue-100 text-blue-800'
: 'bg-purple-100 text-purple-800'
}`}
>
{addr.includes('webtransport') ? '🌐 WebTransport' : addr.includes('webrtc') ? '🔌 WebRTC' : '🚀 QUIC'}
{!isExternalAddress(addr) && ' (Local)'}
</span>
{addr}
</p>
</div>
</div>

<div className="hidden sm:flex sm:flex-col sm:items-end">
<button
onClick={handleConnect}
className={`font-bold py-2 px-4 rounded flex flex-row items-center
${
loading
? 'bg-gray-400 cursor-not-allowed'
: getConnectionPriority() > 3
? 'bg-green-600 hover:bg-green-700'
: 'bg-yellow-600 hover:bg-yellow-700'
}
text-white disabled:opacity-70`}
disabled={loading}
>
{loading && <Spinner />}
<span className="pl-1">
{loading ? 'Connecting...' : getConnectionPriority() > 3 ? 'Connect (Recommended)' : 'Connect'}
</span>
</button>
</div>
</li>
)
}
13 changes: 10 additions & 3 deletions js-peer/src/context/ctx.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'
import { startLibp2p } from '../lib/libp2p'
import { ChatProvider } from './chat-ctx'
import type { Libp2p, PubSub } from '@libp2p/interface'
import type { Libp2p, PubSub, Connection } from '@libp2p/interface'
import type { Identify } from '@libp2p/identify'
import type { DirectMessage } from '@/lib/direct-message'
import type { DelegatedRoutingV1HttpApiClient } from '@helia/delegated-routing-v1-http-api-client'
Expand All @@ -14,9 +14,15 @@ export type Libp2pType = Libp2p<{
delegatedRouting: DelegatedRoutingV1HttpApiClient
}>

export const libp2pContext = createContext<{ libp2p: Libp2pType }>({
export const libp2pContext = createContext<{
libp2p: Libp2pType
connections: Connection[]
setConnections: (connections: Connection[]) => void
}>({
// @ts-ignore to avoid having to check isn't undefined everywhere. Can't be undefined because children are conditionally rendered
libp2p: undefined,
connections: [],
setConnections: () => {},
})

interface WrapperProps {
Expand All @@ -28,6 +34,7 @@ let loaded = false
export function AppWrapper({ children }: WrapperProps) {
const [libp2p, setLibp2p] = useState<Libp2pType | undefined>(undefined)
const [error, setError] = useState('')
const [connections, setConnections] = useState<Connection[]>([])

useEffect(() => {
const init = async () => {
Expand Down Expand Up @@ -57,7 +64,7 @@ export function AppWrapper({ children }: WrapperProps) {
}

return (
<libp2pContext.Provider value={{ libp2p }}>
<libp2pContext.Provider value={{ libp2p, connections, setConnections }}>
<ChatProvider>{children}</ChatProvider>
</libp2pContext.Provider>
)
Expand Down
Loading