diff --git a/packages/svelte/src/lib/components/gun/relay/GunRelay.svelte b/packages/svelte/src/lib/components/gun/relay/GunRelay.svelte new file mode 100644 index 0000000..b42c617 --- /dev/null +++ b/packages/svelte/src/lib/components/gun/relay/GunRelay.svelte @@ -0,0 +1,80 @@ + + +
+ + + + {#if $relay.peer} +
+ Relay connesso: {$relay.peer} +
+ {/if} + + (open = false)}> +
+
+
+
+ Host: + + + +
+ + {#if $relay.status !== 'offline'} +
+
Relay server is {$relay.status} for {$relay.age}
+
Delay: {$relay.delay} ms
+
Pulse drift: {$relay.lag} ms
+
Active wires: {$relay.activeWires} / {$relay.totalConnections}
+
Data storage is {$relay.store ? 'enabled' : 'disabled'}
+
+ {/if} +
+ +
+
+
+ + diff --git a/packages/svelte/src/lib/components/gun/relay/GunRelayList.svelte b/packages/svelte/src/lib/components/gun/relay/GunRelayList.svelte new file mode 100644 index 0000000..6e5c7ed --- /dev/null +++ b/packages/svelte/src/lib/components/gun/relay/GunRelayList.svelte @@ -0,0 +1,41 @@ + + +
+
+
Volunteer relay peers:
+
+ +
+ +
+ + \ No newline at end of file diff --git a/packages/svelte/src/lib/components/gun/ui/UiLayer.svelte b/packages/svelte/src/lib/components/gun/ui/UiLayer.svelte new file mode 100644 index 0000000..96630c3 --- /dev/null +++ b/packages/svelte/src/lib/components/gun/ui/UiLayer.svelte @@ -0,0 +1,55 @@ + + + {#if open} +
+ {#if back} +
e.key === 'Enter' && close()} + tabindex="0" + role="button" + aria-label="Chiudi" + transition:fade + >
+ {/if} +
+ {#if closeButton} + + {/if} + +
+
+ {/if} + + \ No newline at end of file diff --git a/packages/svelte/src/lib/components/gun/user/UserGraph.svelte b/packages/svelte/src/lib/components/gun/user/UserGraph.svelte new file mode 100644 index 0000000..43907e8 --- /dev/null +++ b/packages/svelte/src/lib/components/gun/user/UserGraph.svelte @@ -0,0 +1,76 @@ + + +
+

User Graph

+ + {#each Object.entries(graph) as [id, node]} +
+
toggleShow(node)}> + {#if id[0] === '~'} + + {/if} +
{id[0] === '~' ? id.slice(88) : id}
+
+ {#if node.show} +
+ {#each Object.entries(node) as [key, value]} + {#if key !== '_' && key !== 'show'} +
+
{key}
+
{formatValue(value)}
+
+ {/if} + {/each} +
+ {/if} +
+ {/each} +
+ + \ No newline at end of file diff --git a/packages/svelte/src/lib/gun/auth.ts b/packages/svelte/src/lib/gun/auth.ts index 1136ba5..63eb112 100644 --- a/packages/svelte/src/lib/gun/auth.ts +++ b/packages/svelte/src/lib/gun/auth.ts @@ -7,6 +7,7 @@ import type { IGunUserInstance } from "gun/types"; import { auth, leave, useUser, isPair } from "./user"; import { useGun } from "./gun"; import SEA from "gun/sea"; +import { browser } from "$app/environment"; const MESSAGE_TO_SIGN = "Accesso a GunDB con Ethereum"; export function initializeAuth() { @@ -229,8 +230,11 @@ pass.update(p => ({ })); function genLink(text = "", auth_url = "#/auth/") { - let base = encodeURIComponent(text); - return window.location.origin + window.location.pathname + auth_url + base; + if (browser) { + let base = encodeURIComponent(text); + + return window.location.origin + window.location.pathname + auth_url + base; +} } export function parseLink(link: string, auth_url = "#/auth/") { diff --git a/packages/svelte/src/lib/gun/gun.ts b/packages/svelte/src/lib/gun/gun.ts index 42a4a36..48543e1 100644 --- a/packages/svelte/src/lib/gun/gun.ts +++ b/packages/svelte/src/lib/gun/gun.ts @@ -12,6 +12,7 @@ import "gun/lib/store"; import "gun/lib/rindexed"; import "gun/lib/webrtc"; import "gun-eth"; +import "gun/lib/yson.js"; import { peers, validToken } from "../../../gun.config"; diff --git a/packages/svelte/src/lib/gun/relay.ts b/packages/svelte/src/lib/gun/relay.ts new file mode 100644 index 0000000..eb4967e --- /dev/null +++ b/packages/svelte/src/lib/gun/relay.ts @@ -0,0 +1,115 @@ +/** + * Relay connection management + * @module Relay + * @group Database + */ + +import { useGun } from "./gun"; +import { derived, writable, get } from "svelte/store"; +import { browser } from "$app/environment"; +import ms from "ms"; + +import {peers} from "../../../gun.config" + +const defaultPeer = peers[0]; + +/** + * Peer server status reactive object + * @typedef {Object} Relay + * @property {string} peer + * @property {string} hostname + * @property {string} status + * @property {number} pulse + * @property {number} lag + * @property {number} started + * @property {number} diff + * @property {string} age + * @property {boolean} blink + */ + +function createRelay() { + const store = writable({ + list: [], + peer: browser ? localStorage.getItem("peer") || defaultPeer : defaultPeer, + hostname: "", + status: "offline", + started: 0, + pulse: 0, + lag: 0, + diff: 0, + age: "", + delay: 0, + blink: false + }); + + const { subscribe, update } = store; + + return { + subscribe, + setPeer: (url: string) => { + update(r => { + r.peer = url; + if (browser) localStorage.setItem("peer", url); + setTimeout(() => { + //window.location.reload(); + }, 700); + return r; + }); + }, + resetPeer: () => { + update(r => { + r.peer = defaultPeer; + if (browser) localStorage.setItem("peer", defaultPeer); + setTimeout(() => { + //window.location.reload(); + }, 700); + return r; + }); + }, + updateField: (key: string, value: any) => { + update(r => ({ ...r, [key]: value })); + } + }; +} + +export const relay = createRelay(); + +// Derived stores +export const hostname = derived(relay, $relay => new URL($relay.peer)?.hostname || ""); +export const diff = derived(relay, $relay => $relay.pulse - $relay.started); +export const age = derived(diff, $diff => ms($diff)); +export const delay = derived(relay, $relay => Date.now() - $relay.pulse); + +// Watch for pulse changes +let prevPulse = 0; +relay.subscribe($relay => { + if (prevPulse && $relay.pulse !== prevPulse) { + relay.updateField("blink", !$relay.blink); + relay.updateField("lag", $relay.pulse - prevPulse - 500); + } + prevPulse = $relay.pulse; +}); + +/** + * Peer server status monitor + * @returns {{relay: Writable, setPeer: (url: string) => void, resetPeer: () => void}} + */ +export function useRelay() { + const gun = useGun(); + const $relay = get(relay); + + if ($relay.pulse === 0 && hostname) { + gun + .get(get(hostname)) + .map() + .on((d, k) => { + try { + relay.updateField(k, d); + } catch (e) { + console.log(e); + } + }); + } + + return { relay, setPeer: relay.setPeer, resetPeer: relay.resetPeer }; +} \ No newline at end of file diff --git a/packages/svelte/src/lib/gun/relays.ts b/packages/svelte/src/lib/gun/relays.ts new file mode 100644 index 0000000..c15d6c7 --- /dev/null +++ b/packages/svelte/src/lib/gun/relays.ts @@ -0,0 +1,88 @@ +/** + * Loads the [list of active volunteer DHT gun nodes](https://github.com/amark/gun/wiki/volunteer.dht) and benchmarks ping for them + * @module Relays + * @group Database + */ + +/** + * @typedef {Object} Relay + * @property {string} host + * @property {string} url + * @property {string} ping + */ + +/** @typedef {Relay[]} Relays */ + +import urlRegex from "url-regex"; +import { writable, get } from "svelte/store"; +import { relay } from "./relay"; + +function createStore() { + const { subscribe, update } = writable({}); + return { + subscribe, + set: (key: string, value: any) => update(store => ({ ...store, [key]: value })) + }; +} + +const relays = createStore(); +const errors = createStore(); + +/** + * Load the list of the relays + * @param {Object} [options] + * @param {string} [options.source='https://raw.githubusercontent.com/wiki/amark/gun/volunteer.dht.md'] + * @returns {Promise<{}>} + */ +export async function loadRelays({ + source = "https://raw.githubusercontent.com/wiki/amark/gun/volunteer.dht.md", +} = {}): Promise<{}> { + let res = await fetch(source); + let data = await res.text(); + const urls = data.match(urlRegex()); + urls.push(get(relay).peer); + const urlList = Array.from(new Set(urls)); + + urlList.forEach((u) => { + let testUrl = new URL(u); + if (testUrl.pathname === "/gun" && testUrl.pathname.indexOf("~~") === -1) { + let startMoment = performance.now(); + fetch(testUrl.href, { + method: "HEAD", + mode: "cors", + redirect: "follow", + referrerPolicy: "no-referrer", + }) + .then((response) => { + let endMoment = performance.now(); + if (response.ok) { + /** @type {Relay} */ + const rel = { + host: testUrl.hostname, + ping: (endMoment - startMoment).toFixed(), + url: testUrl.href, + }; + relays.set(testUrl.hostname, rel); + } else { + errors.set(testUrl.hostname, response); + } + }) + .catch((e) => { + errors.set(testUrl.hostname, e); + }); + } + }); + + return get(relays); +} + +/** + * Gets the list of actual gun relays and tool to update the list + * @returns {{relays: {}, errors: {}, loadRelays: () => Promise<{}>}} + * @example + * import { useRelays } from '$lib/relays' + * const { relays, errors, loadRelays } = useRelays() + */ +export function useRelays() { + return { relays, errors, loadRelays }; +} \ No newline at end of file diff --git a/packages/svelte/src/routes/auth/+page.svelte b/packages/svelte/src/routes/auth/+page.svelte index e30316f..d2b6d91 100644 --- a/packages/svelte/src/routes/auth/+page.svelte +++ b/packages/svelte/src/routes/auth/+page.svelte @@ -2,24 +2,37 @@ import { wagmiConfig } from "$lib/wagmi"; import { getAccount } from "@wagmi/core"; import { notification } from "$lib/utils/scaffold-eth/notification"; - import { signIn, login, logout } from "$lib/gun/auth"; + import { signIn, login, logout } from "$lib/gun/auth"; import { useUser, loadUserProfile } from "$lib/gun/user"; import { writable } from 'svelte/store'; + import { onMount } from 'svelte'; - import AccountProfile from "$lib/components/gun/account/AccountProfile.svelte"; + //import AccountProfile from "$lib/components/gun/account/AccountProfile.svelte"; import ProfileDisplay from "$lib/components/gun/profile/ProfileDisplay.svelte"; import { useGun } from "$lib/gun/gun"; + import UserGraph from "$lib/components/gun/user/UserGraph.svelte"; + import GunRelay from "$lib/components/gun/relay/GunRelay.svelte"; + import { browser } from "$app/environment"; let errorMessage: string | null = null; - let userPair: Record | null = null; - + let errorTimeoutId: number; + const userPairStore = writable(null); const { user } = useUser(); + let isLoading = true; - let errorTimeoutId: number; + onMount(() => { + isLoading = false; + }); - const userPairStore = writable(null); + let AccountProfile; - + onMount(async () => { + if (browser) { + const module = await import('$lib/components/gun/account/AccountProfile.svelte'); + AccountProfile = module.default; + } + $user.auth = false; + }); function setErrorMessage(message: string | null) { if (errorTimeoutId) clearTimeout(errorTimeoutId); @@ -34,7 +47,6 @@ async function handleSignIn() { const result = await signIn(); if (result === "login") { - // Se signIn restituisce "login", significa che abbiamo trovato un encrypted pair esistente handleLogin(); } else if (result) { setErrorMessage(result); @@ -56,7 +68,7 @@ function handleLogout() { logout(); - userPair = null; + userPairStore.set(null); notification.info("Logout successful!"); } @@ -78,7 +90,6 @@ const pair = await gunInstance.getAndDecryptPair(account.address, signature); - console.log("User Pair:", pair); if (!pair) { setErrorMessage("Unable to retrieve user data"); } else { @@ -89,40 +100,37 @@ } } - $effect(() => { - if (errorMessage) { - notification.error(errorMessage); - } - }); + $: if (errorMessage) { + notification.error(errorMessage); + }
- {#if errorMessage} - - {/if} - - {#if $user?.auth === false} + {#if isLoading} +

Caricamento...

+ {:else if $user?.auth === false}
- - + +
{:else}

👋 Welcome, {$user?.profile?.name || $user?.pub}!

-
-
-
- +
+
+
+ {#if AccountProfile} + + {/if}
-
- +
+ +
+
+ +
+
+
@@ -130,7 +138,6 @@ {#if $userPairStore}

User Pair

-
    {#each Object.entries($userPairStore) as [key, value]}
  • @@ -141,12 +148,11 @@
{/if}
- - + +
{/if} -

How Authentication Works in SE-2Gun

diff --git a/packages/svelte/src/routes/shine/+page.svelte b/packages/svelte/src/routes/shine/+page.svelte index 0ca315c..af51834 100644 --- a/packages/svelte/src/routes/shine/+page.svelte +++ b/packages/svelte/src/routes/shine/+page.svelte @@ -183,8 +183,8 @@ import { ethers } from "ethers";
-
-

Modifica Messaggio Locale

+
+

Modify Off-Chain Message

- {isLoading ? "Modifica in corso..." : "Modifica Messaggio Locale"} + {isLoading ? "Editing in progress..." : "Edit Off-Chain Message"}
@@ -211,21 +211,23 @@ import { ethers } from "ethers";

{error}

{/if} - {#if savedMessage} -
-

Saved Message

+
+ {#if savedMessage} +
+

Saved Message

{savedMessage}

Node ID: {nodeId}

{#if txHash}

Transaction Hash: {txHash}

{/if}
- {/if} + {/if} {#if verificationResult} -
-

Verification Result

-

+

+

Verification Result

+

+ {verificationResult.ok ? "🎉" : "✖️"} {verificationResult.message}

{#if verificationResult.updater} @@ -238,6 +240,7 @@ import { ethers } from "ethers"; {/if}
{/if} +

How to Use Verification

diff --git a/packages/svelte/tailwind.config.cjs b/packages/svelte/tailwind.config.cjs index d9786ef..0824ef8 100644 --- a/packages/svelte/tailwind.config.cjs +++ b/packages/svelte/tailwind.config.cjs @@ -28,7 +28,15 @@ const config = { "ableton-green": "#b6ffc0", "ableton-orange": "#ff764d", "ableton-beige": "#ebf0dc", + "ableton-purple": "#800080", }, + zIndex: { + '100': '100', + '200': '200', + '300': '300', + '400': '400', + '500': '500', + } }, },