diff --git a/public/healthz.json b/public/healthz.json index 04e14bb..672bb2b 100644 --- a/public/healthz.json +++ b/public/healthz.json @@ -2,7 +2,7 @@ "status": "ok", "service": "aitherboard", "version": "0.1.0", - "buildTime": "2026-02-02T13:24:45.619Z", + "buildTime": "2026-02-02T13:56:52.264Z", "gitCommit": "unknown", - "timestamp": 1770038685620 + "timestamp": 1770040612264 } \ No newline at end of file diff --git a/src/lib/components/layout/Header.svelte b/src/lib/components/layout/Header.svelte index 09ebd15..647d2ac 100644 --- a/src/lib/components/layout/Header.svelte +++ b/src/lib/components/layout/Header.svelte @@ -1,22 +1,33 @@ - - - - + + + + + + @@ -24,9 +35,11 @@ Aitherboard - - {#if isLoggedIn} - Logged in as: {currentPubkey?.slice(0, 16)}... + Threads + Feed + {#if isLoggedIn && currentPubkey} + Logged in as + sessionManager.clearSession()} class="px-3 py-1 rounded border border-fog-border dark:border-fog-dark-border bg-fog-post dark:bg-fog-dark-post hover:bg-fog-highlight dark:hover:bg-fog-dark-highlight text-fog-text dark:text-fog-dark-text transition-colors" @@ -36,8 +49,7 @@ {:else} Login {/if} - Feed - Threads + diff --git a/src/lib/components/layout/ProfileBadge.svelte b/src/lib/components/layout/ProfileBadge.svelte index 3056ea6..1595487 100644 --- a/src/lib/components/layout/ProfileBadge.svelte +++ b/src/lib/components/layout/ProfileBadge.svelte @@ -1,5 +1,5 @@ - - - + + + { + // If showing older threads, reload to fetch them + // If hiding older threads, just filter client-side (no reload needed) + if (showOlder) { + loadThreads(); + } + }} + class="mr-2" + /> Show older threads (threads = sortThreads(threads))} class="border border-fog-border dark:border-fog-dark-border bg-fog-post dark:bg-fog-dark-post text-fog-text dark:text-fog-dark-text px-2 py-1 rounded"> @@ -83,18 +166,58 @@ {#if loading} Loading threads... {:else} + + + + Filter by topic: + (selectedTopic = null)} + class="px-3 py-1 rounded border transition-colors {selectedTopic === null + ? 'bg-fog-accent dark:bg-fog-dark-accent text-white border-fog-accent dark:border-fog-dark-accent' + : 'bg-fog-post dark:bg-fog-dark-post text-fog-text dark:text-fog-dark-text border-fog-border dark:border-fog-dark-border hover:bg-fog-highlight dark:hover:bg-fog-dark-highlight'}" + > + All ({filterByAge(threads).length}) + + {#each getTopicsWithCounts() as { topic, count }} + (selectedTopic = topic === null ? undefined : topic)} + class="px-3 py-1 rounded border transition-colors {selectedTopic === (topic === null ? undefined : topic) + ? 'bg-fog-accent dark:bg-fog-dark-accent text-white border-fog-accent dark:border-fog-dark-accent' + : 'bg-fog-post dark:bg-fog-dark-post text-fog-text dark:text-fog-dark-text border-fog-border dark:border-fog-dark-border hover:bg-fog-highlight dark:hover:bg-fog-dark-highlight'}" + > + {topic === null ? 'General' : topic} ({count}) + + {/each} + + + + - General - {#each getThreadsByTopic(null) as thread} - - {/each} - - {#each getTopics() as topic} - {topic} - {#each getThreadsByTopic(topic) as thread} + {#if selectedTopic === null} + + General + {#each getThreadsByTopic(null) as thread} + + {/each} + + {#each getTopics() as topic} + {topic} + {#each getThreadsByTopic(topic) as thread} + + {/each} + {/each} + {:else} + + + {selectedTopic === undefined ? 'General' : selectedTopic} + + {#each getFilteredThreads() as thread} {/each} - {/each} + {#if getFilteredThreads().length === 0} + No threads found in this topic. + {/if} + {/if} {/if} diff --git a/src/lib/services/auth/activity-tracker.js b/src/lib/services/auth/activity-tracker.js deleted file mode 100644 index 1d95134..0000000 --- a/src/lib/services/auth/activity-tracker.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Activity tracker - tracks last activity per pubkey - */ -import { eventStore } from '../nostr/event-store.js'; -/** - * Get last activity timestamp for a pubkey - */ -export function getLastActivity(pubkey) { - return eventStore.getLastActivity(pubkey); -} -/** - * Get activity status color - * Red: ≥168 hours (7 days) - * Yellow: ≥48 hours (2 days) but <168 hours - * Green: <48 hours - */ -export function getActivityStatus(pubkey) { - const lastActivity = getLastActivity(pubkey); - if (!lastActivity) - return null; - const now = Math.floor(Date.now() / 1000); - const hoursSince = (now - lastActivity) / 3600; - if (hoursSince >= 168) - return 'red'; - if (hoursSince >= 48) - return 'yellow'; - return 'green'; -} -//# sourceMappingURL=activity-tracker.js.map \ No newline at end of file diff --git a/src/lib/services/auth/activity-tracker.js.map b/src/lib/services/auth/activity-tracker.js.map deleted file mode 100644 index e97cbf9..0000000 --- a/src/lib/services/auth/activity-tracker.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"activity-tracker.js","sourceRoot":"","sources":["activity-tracker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,OAAO,UAAU,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,CAAC,YAAY;QAAE,OAAO,IAAI,CAAC;IAE/B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC1C,MAAM,UAAU,GAAG,CAAC,GAAG,GAAG,YAAY,CAAC,GAAG,IAAI,CAAC;IAE/C,IAAI,UAAU,IAAI,GAAG;QAAE,OAAO,KAAK,CAAC;IACpC,IAAI,UAAU,IAAI,EAAE;QAAE,OAAO,QAAQ,CAAC;IACtC,OAAO,OAAO,CAAC;AACjB,CAAC"} \ No newline at end of file diff --git a/src/lib/services/auth/activity-tracker.ts b/src/lib/services/auth/activity-tracker.ts index e6158bb..d8eef07 100644 --- a/src/lib/services/auth/activity-tracker.ts +++ b/src/lib/services/auth/activity-tracker.ts @@ -7,7 +7,7 @@ import { eventStore } from '../nostr/event-store.js'; /** * Get last activity timestamp for a pubkey */ -export function getLastActivity(pubkey: string): number | undefined { +export async function getLastActivity(pubkey: string): Promise { return eventStore.getLastActivity(pubkey); } @@ -17,8 +17,8 @@ export function getLastActivity(pubkey: string): number | undefined { * Yellow: ≥48 hours (2 days) but <168 hours * Green: <48 hours */ -export function getActivityStatus(pubkey: string): 'red' | 'yellow' | 'green' | null { - const lastActivity = getLastActivity(pubkey); +export async function getActivityStatus(pubkey: string): Promise<'red' | 'yellow' | 'green' | null> { + const lastActivity = await getLastActivity(pubkey); if (!lastActivity) return null; const now = Math.floor(Date.now() / 1000); @@ -28,3 +28,37 @@ export function getActivityStatus(pubkey: string): 'red' | 'yellow' | 'green' | if (hoursSince >= 48) return 'yellow'; return 'green'; } + +/** + * Get activity status message for tooltip + */ +export async function getActivityMessage(pubkey: string): Promise { + const lastActivity = await getLastActivity(pubkey); + if (!lastActivity) return null; + + const now = Math.floor(Date.now() / 1000); + const hoursSince = (now - lastActivity) / 3600; + const daysSince = hoursSince / 24; + + if (hoursSince < 1) { + const minutesSince = Math.floor((now - lastActivity) / 60); + if (minutesSince < 1) { + return 'Active just now'; + } + return `Active ${minutesSince} minute${minutesSince === 1 ? '' : 's'} ago`; + } else if (hoursSince < 24) { + const hours = Math.floor(hoursSince); + return `Active ${hours} hour${hours === 1 ? '' : 's'} ago`; + } else if (daysSince < 7) { + const days = Math.floor(daysSince); + return `Active ${days} day${days === 1 ? '' : 's'} ago`; + } else { + const weeks = Math.floor(daysSince / 7); + if (weeks < 4) { + return `Active ${weeks} week${weeks === 1 ? '' : 's'} ago`; + } else { + const months = Math.floor(daysSince / 30); + return `Active ${months} month${months === 1 ? '' : 's'} ago`; + } + } +} diff --git a/src/lib/services/auth/anonymous-signer.js b/src/lib/services/auth/anonymous-signer.js deleted file mode 100644 index 743a1bd..0000000 --- a/src/lib/services/auth/anonymous-signer.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Anonymous signer (generated keys, NIP-49 encrypted) - */ -import { generatePrivateKey } from '../security/key-management.js'; -import { storeAnonymousKey, getAnonymousKey } from '../cache/anonymous-key-store.js'; -import { getPublicKeyFromNsec } from './nsec-signer.js'; -import { signEventWithNsec } from './nsec-signer.js'; -/** - * Generate and store anonymous key - */ -export async function generateAnonymousKey(password) { - const nsec = generatePrivateKey(); - const pubkey = await getPublicKeyFromNsec(nsec); - // Store encrypted - await storeAnonymousKey(nsec, password, pubkey); - return { pubkey, nsec }; -} -/** - * Get stored anonymous key - */ -export async function getStoredAnonymousKey(pubkey, password) { - return getAnonymousKey(pubkey, password); -} -/** - * Sign event with anonymous key - */ -export async function signEventWithAnonymous(event, pubkey, password) { - const nsec = await getStoredAnonymousKey(pubkey, password); - if (!nsec) { - throw new Error('Anonymous key not found'); - } - // For anonymous keys, we need the ncryptsec format - // This is simplified - in practice we'd store ncryptsec and decrypt it - // For now, assume we have the plain nsec after decryption - return signEventWithNsec(event, nsec, password); -} -/** - * Generate anonymous handle - */ -export function generateAnonymousHandle(pubkey) { - // Use last 6 characters of pubkey for uniqueness - const suffix = pubkey.slice(-6); - return `Aitherite${suffix}`; -} -//# sourceMappingURL=anonymous-signer.js.map \ No newline at end of file diff --git a/src/lib/services/auth/anonymous-signer.js.map b/src/lib/services/auth/anonymous-signer.js.map deleted file mode 100644 index 3c3ac9b..0000000 --- a/src/lib/services/auth/anonymous-signer.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"anonymous-signer.js","sourceRoot":"","sources":["anonymous-signer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACrF,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,QAAgB;IAIzD,MAAM,IAAI,GAAG,kBAAkB,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAEhD,kBAAkB;IAClB,MAAM,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAEhD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAAc,EACd,QAAgB;IAEhB,OAAO,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAC3C,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,KAAqC,EACrC,MAAc,EACd,QAAgB;IAEhB,MAAM,IAAI,GAAG,MAAM,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC3D,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,mDAAmD;IACnD,uEAAuE;IACvE,0DAA0D;IAC1D,OAAO,iBAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;AAClD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAc;IACpD,iDAAiD;IACjD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChC,OAAO,YAAY,MAAM,EAAE,CAAC;AAC9B,CAAC"} \ No newline at end of file diff --git a/src/lib/services/auth/bunker-signer.js b/src/lib/services/auth/bunker-signer.js deleted file mode 100644 index ce3862f..0000000 --- a/src/lib/services/auth/bunker-signer.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * NIP-46 Bunker signer (remote signer) - */ -/** - * Connect to bunker signer - */ -export async function connectBunker(bunkerUri) { - // Parse bunker:// URI - // Format: bunker://@?token= - const match = bunkerUri.match(/^bunker:\/\/([^@]+)@([^?]+)(?:\?token=([^&]+))?$/); - if (!match) { - throw new Error('Invalid bunker URI'); - } - const [, pubkey, relay, token] = match; - return { - bunkerUrl: relay, - pubkey, - token - }; -} -/** - * Sign event with bunker - */ -export async function signEventWithBunker(event, connection) { - // Placeholder - would: - // 1. Send NIP-46 request to bunker - // 2. Wait for response - // 3. Return signed event - throw new Error('Bunker signing not yet implemented'); -} -//# sourceMappingURL=bunker-signer.js.map \ No newline at end of file diff --git a/src/lib/services/auth/bunker-signer.js.map b/src/lib/services/auth/bunker-signer.js.map deleted file mode 100644 index c0c6fe1..0000000 --- a/src/lib/services/auth/bunker-signer.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"bunker-signer.js","sourceRoot":"","sources":["bunker-signer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAiB;IACnD,sBAAsB;IACtB,kDAAkD;IAClD,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;IAClF,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC;IAEvC,OAAO;QACL,SAAS,EAAE,KAAK;QAChB,MAAM;QACN,KAAK;KACN,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,KAAqC,EACrC,UAA4B;IAE5B,uBAAuB;IACvB,mCAAmC;IACnC,uBAAuB;IACvB,yBAAyB;IAEzB,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;AACxD,CAAC"} \ No newline at end of file diff --git a/src/lib/services/auth/nip07-signer.js b/src/lib/services/auth/nip07-signer.js deleted file mode 100644 index 2e7b342..0000000 --- a/src/lib/services/auth/nip07-signer.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * NIP-07 signer (browser extension) - */ -/** - * Check if NIP-07 is available - */ -export function isNIP07Available() { - return typeof window !== 'undefined' && 'nostr' in window; -} -/** - * Get NIP-07 signer - */ -export function getNIP07Signer() { - if (!isNIP07Available()) - return null; - const nostr = window.nostr; - return nostr || null; -} -/** - * Sign event with NIP-07 - */ -export async function signEventWithNIP07(event) { - const signer = getNIP07Signer(); - if (!signer) { - throw new Error('NIP-07 not available'); - } - return signer.signEvent(event); -} -/** - * Get public key with NIP-07 - */ -export async function getPublicKeyWithNIP07() { - const signer = getNIP07Signer(); - if (!signer) { - throw new Error('NIP-07 not available'); - } - return signer.getPublicKey(); -} -//# sourceMappingURL=nip07-signer.js.map \ No newline at end of file diff --git a/src/lib/services/auth/nip07-signer.js.map b/src/lib/services/auth/nip07-signer.js.map deleted file mode 100644 index 7889cb0..0000000 --- a/src/lib/services/auth/nip07-signer.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"nip07-signer.js","sourceRoot":"","sources":["nip07-signer.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,IAAI,MAAM,CAAC;AAC5D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC,gBAAgB,EAAE;QAAE,OAAO,IAAI,CAAC;IAErC,MAAM,KAAK,GAAI,MAAkC,CAAC,KAAK,CAAC;IACxD,OAAO,KAAK,IAAI,IAAI,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAqC;IAErC,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;IAChC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;IAChC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,MAAM,CAAC,YAAY,EAAE,CAAC;AAC/B,CAAC"} \ No newline at end of file diff --git a/src/lib/services/auth/nsec-signer.js b/src/lib/services/auth/nsec-signer.js deleted file mode 100644 index 48ad42c..0000000 --- a/src/lib/services/auth/nsec-signer.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Nsec signer (direct private key, NIP-49 encrypted) - */ -import { decryptPrivateKey } from '../security/key-management.js'; -/** - * Sign event with nsec (private key) - * This is a placeholder - full implementation requires: - * - secp256k1 cryptography library - * - Event ID computation (SHA256) - * - Signature computation - */ -export async function signEventWithNsec(event, ncryptsec, password) { - // Decrypt private key - const nsec = await decryptPrivateKey(ncryptsec, password); - // Compute event ID (SHA256 of serialized event) - const serialized = JSON.stringify([ - 0, - event.pubkey, - event.created_at, - event.kind, - event.tags, - event.content - ]); - const encoder = new TextEncoder(); - const data = encoder.encode(serialized); - const hashBuffer = await crypto.subtle.digest('SHA-256', data); - const hashArray = Array.from(new Uint8Array(hashBuffer)); - const id = hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); - // TEMPORARY: Generate a deterministic signature-like string - // This is NOT a valid secp256k1 signature but has the correct length - // Production code MUST compute actual secp256k1 signature - const sigData = encoder.encode(nsec + id); - const sigHashBuffer = await crypto.subtle.digest('SHA-256', sigData); - const sigHashArray = Array.from(new Uint8Array(sigHashBuffer)); - const sigHash = sigHashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); - // Double the hash to get 128 chars (64 * 2) - const sig = (sigHash + sigHash).slice(0, 128); - return { - ...event, - id, - sig - }; -} -/** - * Get public key from private key - * - * TEMPORARY: Uses SHA256 hash of private key to generate a deterministic pubkey. - * This is NOT a valid secp256k1 public key derivation but provides unique pubkeys. - * Production code MUST use proper secp256k1 point multiplication. - */ -export async function getPublicKeyFromNsec(nsec) { - // TEMPORARY: Generate deterministic pubkey from private key hash - // This ensures each private key gets a unique (but not cryptographically valid) pubkey - const encoder = new TextEncoder(); - const data = encoder.encode(nsec); - const hashBuffer = await crypto.subtle.digest('SHA-256', data); - const hashArray = Array.from(new Uint8Array(hashBuffer)); - const hash = hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); - // Double the hash to get 64 chars (32 * 2) - return (hash + hash).slice(0, 64); -} -//# sourceMappingURL=nsec-signer.js.map \ No newline at end of file diff --git a/src/lib/services/auth/nsec-signer.js.map b/src/lib/services/auth/nsec-signer.js.map deleted file mode 100644 index 30d469f..0000000 --- a/src/lib/services/auth/nsec-signer.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"nsec-signer.js","sourceRoot":"","sources":["nsec-signer.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/lib/services/auth/profile-fetcher.js b/src/lib/services/auth/profile-fetcher.js deleted file mode 100644 index d79a85b..0000000 --- a/src/lib/services/auth/profile-fetcher.js +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Profile fetcher (kind 0 events) - */ -import { nostrClient } from '../nostr/applesauce-client.js'; -import { cacheProfile, getProfile, getProfiles } from '../cache/profile-cache.js'; -import { config } from '../nostr/config.js'; -/** - * Parse profile from kind 0 event - */ -export function parseProfile(event) { - const profile = {}; - // Try to parse from tags first (preferred) - const nameTag = event.tags.find((t) => t[0] === 'name'); - if (nameTag && nameTag[1]) - profile.name = nameTag[1]; - const aboutTag = event.tags.find((t) => t[0] === 'about'); - if (aboutTag && aboutTag[1]) - profile.about = aboutTag[1]; - const pictureTag = event.tags.find((t) => t[0] === 'picture'); - if (pictureTag && pictureTag[1]) - profile.picture = pictureTag[1]; - // Multiple tags for website, nip05, lud16 - profile.website = event.tags.filter((t) => t[0] === 'website').map((t) => t[1]).filter(Boolean); - profile.nip05 = event.tags.filter((t) => t[0] === 'nip05').map((t) => t[1]).filter(Boolean); - profile.lud16 = event.tags.filter((t) => t[0] === 'lud16').map((t) => t[1]).filter(Boolean); - // Fallback to JSON content if tags not found - if (!profile.name || !profile.about) { - try { - const json = JSON.parse(event.content); - if (json.name && !profile.name) - profile.name = json.name; - if (json.about && !profile.about) - profile.about = json.about; - if (json.picture && !profile.picture) - profile.picture = json.picture; - if (json.website && profile.website.length === 0) { - profile.website = Array.isArray(json.website) ? json.website : [json.website]; - } - if (json.nip05 && profile.nip05.length === 0) { - profile.nip05 = Array.isArray(json.nip05) ? json.nip05 : [json.nip05]; - } - if (json.lud16 && profile.lud16.length === 0) { - profile.lud16 = Array.isArray(json.lud16) ? json.lud16 : [json.lud16]; - } - } - catch { - // Invalid JSON, ignore - } - } - return profile; -} -/** - * Fetch profile for a pubkey - */ -export async function fetchProfile(pubkey, relays) { - // Try cache first - const cached = await getProfile(pubkey); - if (cached) { - return parseProfile(cached.event); - } - // Fetch from relays - const relayList = relays || [ - ...config.defaultRelays, - ...config.profileRelays - ]; - const events = await nostrClient.fetchEvents([{ kinds: [0], authors: [pubkey], limit: 1 }], relayList, { useCache: true, cacheResults: true }); - if (events.length === 0) - return null; - const event = events[0]; - await cacheProfile(event); - return parseProfile(event); -} -/** - * Fetch multiple profiles - */ -export async function fetchProfiles(pubkeys, relays) { - const profiles = new Map(); - // Check cache first - const cached = await getProfiles(pubkeys); - for (const [pubkey, cachedProfile] of cached.entries()) { - profiles.set(pubkey, parseProfile(cachedProfile.event)); - } - // Fetch missing profiles - const missing = pubkeys.filter((p) => !profiles.has(p)); - if (missing.length === 0) - return profiles; - const relayList = relays || [ - ...config.defaultRelays, - ...config.profileRelays - ]; - const events = await nostrClient.fetchEvents([{ kinds: [0], authors: missing, limit: 1 }], relayList, { useCache: true, cacheResults: true }); - for (const event of events) { - await cacheProfile(event); - profiles.set(event.pubkey, parseProfile(event)); - } - return profiles; -} -//# sourceMappingURL=profile-fetcher.js.map \ No newline at end of file diff --git a/src/lib/services/auth/profile-fetcher.js.map b/src/lib/services/auth/profile-fetcher.js.map deleted file mode 100644 index 2bb0fe7..0000000 --- a/src/lib/services/auth/profile-fetcher.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"profile-fetcher.js","sourceRoot":"","sources":["profile-fetcher.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/lib/services/auth/relay-list-fetcher.js b/src/lib/services/auth/relay-list-fetcher.js deleted file mode 100644 index 906aa88..0000000 --- a/src/lib/services/auth/relay-list-fetcher.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Relay list fetcher (kind 10002 and 10432) - */ -import { nostrClient } from '../nostr/applesauce-client.js'; -import { config } from '../nostr/config.js'; -/** - * Parse relay list from event - */ -export function parseRelayList(event) { - const relays = []; - for (const tag of event.tags) { - if (tag[0] === 'r' && tag[1]) { - const url = tag[1]; - const markers = tag.slice(2); - // If no markers, relay is both read and write - if (markers.length === 0) { - relays.push({ url, read: true, write: true }); - continue; - } - // Check for explicit markers - const hasRead = markers.includes('read'); - const hasWrite = markers.includes('write'); - // If only 'read' marker: read=true, write=false - // If only 'write' marker: read=false, write=true - // If both or neither explicitly: both true (default behavior) - const read = hasRead || (!hasRead && !hasWrite); - const write = hasWrite || (!hasRead && !hasWrite); - relays.push({ url, read, write }); - } - } - return relays; -} -/** - * Fetch relay lists for a pubkey (kind 10002 and 10432) - */ -export async function fetchRelayLists(pubkey, relays) { - const relayList = relays || [ - ...config.defaultRelays, - ...config.profileRelays - ]; - // Fetch both kind 10002 and 10432 - const events = await nostrClient.fetchEvents([ - { kinds: [10002], authors: [pubkey], limit: 1 }, - { kinds: [10432], authors: [pubkey], limit: 1 } - ], relayList, { useCache: true, cacheResults: true }); - const inbox = []; - const outbox = []; - for (const event of events) { - const relayInfos = parseRelayList(event); - for (const info of relayInfos) { - if (info.read && !inbox.includes(info.url)) { - inbox.push(info.url); - } - if (info.write && !outbox.includes(info.url)) { - outbox.push(info.url); - } - } - } - // Deduplicate - return { - inbox: [...new Set(inbox)], - outbox: [...new Set(outbox)] - }; -} -//# sourceMappingURL=relay-list-fetcher.js.map \ No newline at end of file diff --git a/src/lib/services/auth/relay-list-fetcher.js.map b/src/lib/services/auth/relay-list-fetcher.js.map deleted file mode 100644 index 7ef9d12..0000000 --- a/src/lib/services/auth/relay-list-fetcher.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"relay-list-fetcher.js","sourceRoot":"","sources":["relay-list-fetcher.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/lib/services/auth/session-manager.js b/src/lib/services/auth/session-manager.js deleted file mode 100644 index 45f652a..0000000 --- a/src/lib/services/auth/session-manager.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Session manager for active user sessions - */ -// Simple store implementation for Svelte reactivity -function createStore(initial) { - let value = initial; - const subscribers = new Set(); - return { - get value() { - return value; - }, - set(newValue) { - value = newValue; - subscribers.forEach((fn) => fn(value)); - }, - subscribe(fn) { - subscribers.add(fn); - fn(value); - return () => subscribers.delete(fn); - } - }; -} -class SessionManager { - currentSession = null; - session = createStore(null); - /** - * Set current session - */ - setSession(session) { - this.currentSession = session; - this.session.set(session); - // Store in localStorage for persistence - if (typeof window !== 'undefined') { - localStorage.setItem('aitherboard_session', JSON.stringify({ - pubkey: session.pubkey, - method: session.method, - createdAt: session.createdAt - })); - } - } - /** - * Get current session - */ - getSession() { - return this.currentSession; - } - /** - * Check if user is logged in - */ - isLoggedIn() { - return this.currentSession !== null; - } - /** - * Get current pubkey - */ - getCurrentPubkey() { - return this.currentSession?.pubkey || null; - } - /** - * Sign event with current session - */ - async signEvent(event) { - if (!this.currentSession) { - throw new Error('No active session'); - } - return this.currentSession.signer(event); - } - /** - * Clear session - */ - clearSession() { - this.currentSession = null; - this.session.set(null); - if (typeof window !== 'undefined') { - localStorage.removeItem('aitherboard_session'); - } - } - /** - * Restore session from localStorage - */ - async restoreSession() { - if (typeof window === 'undefined') - return false; - const stored = localStorage.getItem('aitherboard_session'); - if (!stored) - return false; - try { - const data = JSON.parse(stored); - // Session restoration would require re-initializing the signer - // This is simplified - full implementation would restore the signer - return false; - } - catch { - return false; - } - } -} -export const sessionManager = new SessionManager(); -//# sourceMappingURL=session-manager.js.map \ No newline at end of file diff --git a/src/lib/services/auth/session-manager.js.map b/src/lib/services/auth/session-manager.js.map deleted file mode 100644 index 5d7beb3..0000000 --- a/src/lib/services/auth/session-manager.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"session-manager.js","sourceRoot":"","sources":["session-manager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/lib/services/auth/session-manager.ts b/src/lib/services/auth/session-manager.ts index 98ef605..cee44f2 100644 --- a/src/lib/services/auth/session-manager.ts +++ b/src/lib/services/auth/session-manager.ts @@ -41,16 +41,21 @@ class SessionManager { /** * Set current session */ - setSession(session: UserSession): void { + setSession(session: UserSession, metadata?: Record): void { this.currentSession = session; this.session.set(session); // Store in localStorage for persistence if (typeof window !== 'undefined') { - localStorage.setItem('aitherboard_session', JSON.stringify({ + const sessionData: any = { pubkey: session.pubkey, method: session.method, createdAt: session.createdAt - })); + }; + // Store method-specific metadata for restoration + if (metadata) { + sessionData.metadata = metadata; + } + localStorage.setItem('aitherboard_session', JSON.stringify(sessionData)); } } @@ -99,6 +104,7 @@ class SessionManager { /** * Restore session from localStorage + * This will attempt to restore the session based on the auth method */ async restoreSession(): Promise { if (typeof window === 'undefined') return false; @@ -108,10 +114,100 @@ class SessionManager { try { const data = JSON.parse(stored); - // Session restoration would require re-initializing the signer - // This is simplified - full implementation would restore the signer - return false; - } catch { + const { pubkey, method, metadata } = data; + + if (!pubkey || !method) return false; + + // Import auth handlers dynamically to avoid circular dependencies + switch (method) { + case 'nip07': { + // For NIP-07, we can restore by checking if extension is still available + const { isNIP07Available, getPublicKeyWithNIP07, signEventWithNIP07 } = await import('./nip07-signer.js'); + if (isNIP07Available()) { + try { + // Verify the extension still has the same pubkey + const currentPubkey = await getPublicKeyWithNIP07(); + if (currentPubkey === pubkey) { + this.setSession({ + pubkey, + method: 'nip07', + signer: signEventWithNIP07, + createdAt: data.createdAt || Date.now() + }); + return true; + } + } catch { + // Extension error, can't restore + return false; + } + } + return false; + } + case 'bunker': { + // For bunker, restore if we have the bunker URI + if (metadata?.bunkerUri) { + const { connectBunker, signEventWithBunker } = await import('./bunker-signer.js'); + try { + const connection = await connectBunker(metadata.bunkerUri); + if (connection.pubkey === pubkey) { + this.setSession({ + pubkey, + method: 'bunker', + signer: async (event) => signEventWithBunker(event, connection), + createdAt: data.createdAt || Date.now() + }, { bunkerUri: metadata.bunkerUri }); + return true; + } + } catch { + // Bunker connection failed + return false; + } + } + return false; + } + case 'anonymous': { + // For anonymous, we can restore if the encrypted key is stored + // The key is stored in IndexedDB, we just need to verify it exists + const { getStoredAnonymousKey } = await import('./anonymous-signer.js'); + // We can't restore without password, but we can check if key exists + // For now, we'll just restore the pubkey and let signer fail if password is wrong + // In practice, user would need to re-enter password + if (pubkey) { + // Check if key exists in storage + try { + // This will fail without password, but we can still restore session + // The signer will need password on first use + const { signEventWithAnonymous } = await import('./anonymous-signer.js'); + this.setSession({ + pubkey, + method: 'anonymous', + signer: async (event) => { + // This will fail without password - user needs to re-authenticate + throw new Error('Anonymous session requires password. Please log in again.'); + }, + createdAt: data.createdAt || Date.now() + }); + // Note: This session won't work until user re-authenticates + return true; + } catch { + return false; + } + } + return false; + } + case 'nsec': { + // nsec can't be restored without password (security) + // Clear the stored session + localStorage.removeItem('aitherboard_session'); + return false; + } + default: + return false; + } + } catch (error) { + console.error('Error restoring session:', error); + // Clear corrupted session data + localStorage.removeItem('aitherboard_session'); return false; } } diff --git a/src/lib/services/auth/user-preferences-fetcher.js b/src/lib/services/auth/user-preferences-fetcher.js deleted file mode 100644 index e50a7b8..0000000 --- a/src/lib/services/auth/user-preferences-fetcher.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * User preferences fetcher - * Placeholder for future user preference events - */ -/** - * Fetch user preferences - */ -export async function fetchUserPreferences(pubkey) { - // Placeholder - would fetch preference events - return null; -} -//# sourceMappingURL=user-preferences-fetcher.js.map \ No newline at end of file diff --git a/src/lib/services/auth/user-preferences-fetcher.js.map b/src/lib/services/auth/user-preferences-fetcher.js.map deleted file mode 100644 index efccf0a..0000000 --- a/src/lib/services/auth/user-preferences-fetcher.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"user-preferences-fetcher.js","sourceRoot":"","sources":["user-preferences-fetcher.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,MAAc;IACvD,8CAA8C;IAC9C,OAAO,IAAI,CAAC;AACd,CAAC"} \ No newline at end of file diff --git a/src/lib/services/auth/user-status-fetcher.js b/src/lib/services/auth/user-status-fetcher.js deleted file mode 100644 index 02f5c83..0000000 --- a/src/lib/services/auth/user-status-fetcher.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * User status fetcher (kind 30315, NIP-38) - */ -import { nostrClient } from '../nostr/applesauce-client.js'; -import { config } from '../nostr/config.js'; -/** - * Parse user status from kind 30315 event - */ -export function parseUserStatus(event) { - if (event.kind !== 30315) - return null; - // Check for d tag with value "general" - const dTag = event.tags.find((t) => t[0] === 'd' && t[1] === 'general'); - if (!dTag) - return null; - return event.content || null; -} -/** - * Fetch user status for a pubkey - */ -export async function fetchUserStatus(pubkey, relays) { - const relayList = relays || [ - ...config.defaultRelays, - ...config.profileRelays - ]; - const events = await nostrClient.fetchEvents([ - { - kinds: [30315], - authors: [pubkey], - '#d': ['general'], - limit: 1 - } - ], relayList, { useCache: true, cacheResults: true }); - if (events.length === 0) - return null; - return parseUserStatus(events[0]); -} -//# sourceMappingURL=user-status-fetcher.js.map \ No newline at end of file diff --git a/src/lib/services/auth/user-status-fetcher.js.map b/src/lib/services/auth/user-status-fetcher.js.map deleted file mode 100644 index d4031a4..0000000 --- a/src/lib/services/auth/user-status-fetcher.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"user-status-fetcher.js","sourceRoot":"","sources":["user-status-fetcher.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAG5C;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAiB;IAC/C,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IAEtC,uCAAuC;IACvC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;IACxE,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,OAAO,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAc,EACd,MAAiB;IAEjB,MAAM,SAAS,GAAG,MAAM,IAAI;QAC1B,GAAG,MAAM,CAAC,aAAa;QACvB,GAAG,MAAM,CAAC,aAAa;KACxB,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,WAAW,CAC1C;QACE;YACE,KAAK,EAAE,CAAC,KAAK,CAAC;YACd,OAAO,EAAE,CAAC,MAAM,CAAC;YACjB,IAAI,EAAE,CAAC,SAAS,CAAC;YACjB,KAAK,EAAE,CAAC;SACT;KACF,EACD,SAAS,EACT,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CACvC,CAAC;IAEF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAErC,OAAO,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC"} \ No newline at end of file diff --git a/src/lib/services/cache/anonymous-key-store.js b/src/lib/services/cache/anonymous-key-store.js deleted file mode 100644 index 2d92fac..0000000 --- a/src/lib/services/cache/anonymous-key-store.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Anonymous key storage (NIP-49 encrypted) - */ -import { getDB } from './indexeddb-store.js'; -import { encryptPrivateKey, decryptPrivateKey } from '../security/key-management.js'; -/** - * Store an anonymous key (encrypted) - */ -export async function storeAnonymousKey(nsec, password, pubkey) { - const ncryptsec = await encryptPrivateKey(nsec, password); - const db = await getDB(); - const stored = { - id: pubkey, - ncryptsec, - pubkey, - created_at: Date.now() - }; - await db.put('keys', stored); -} -/** - * Retrieve and decrypt an anonymous key - */ -export async function getAnonymousKey(pubkey, password) { - const db = await getDB(); - const stored = await db.get('keys', pubkey); - if (!stored) - return null; - const key = stored; - return decryptPrivateKey(key.ncryptsec, password); -} -/** - * List all stored anonymous keys (pubkeys only) - */ -export async function listAnonymousKeys() { - const db = await getDB(); - const keys = []; - const tx = db.transaction('keys', 'readonly'); - for await (const cursor of tx.store.iterate()) { - const key = cursor.value; - keys.push(key.pubkey); - } - await tx.done; - return keys; -} -/** - * Delete an anonymous key - */ -export async function deleteAnonymousKey(pubkey) { - const db = await getDB(); - await db.delete('keys', pubkey); -} -//# sourceMappingURL=anonymous-key-store.js.map \ No newline at end of file diff --git a/src/lib/services/cache/anonymous-key-store.js.map b/src/lib/services/cache/anonymous-key-store.js.map deleted file mode 100644 index 87d4075..0000000 --- a/src/lib/services/cache/anonymous-key-store.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"anonymous-key-store.js","sourceRoot":"","sources":["anonymous-key-store.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AASrF;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAY,EACZ,QAAgB,EAChB,MAAc;IAEd,MAAM,SAAS,GAAG,MAAM,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC1D,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,MAAM,GAAuB;QACjC,EAAE,EAAE,MAAM;QACV,SAAS;QACT,MAAM;QACN,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;KACvB,CAAC;IACF,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAc,EACd,QAAgB;IAEhB,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,MAAM,GAAG,GAAG,MAA4B,CAAC;IACzC,OAAO,iBAAiB,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAE9C,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAC9C,MAAM,GAAG,GAAG,MAAM,CAAC,KAA2B,CAAC;QAC/C,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAED,MAAM,EAAE,CAAC,IAAI,CAAC;IACd,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAc;IACrD,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAClC,CAAC"} \ No newline at end of file diff --git a/src/lib/services/cache/event-cache.js b/src/lib/services/cache/event-cache.js deleted file mode 100644 index e78cf5f..0000000 --- a/src/lib/services/cache/event-cache.js +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Event caching with IndexedDB - */ -import { getDB } from './indexeddb-store.js'; -/** - * Store an event in cache - */ -export async function cacheEvent(event) { - const db = await getDB(); - const cached = { - ...event, - cached_at: Date.now() - }; - await db.put('events', cached); -} -/** - * Store multiple events in cache - */ -export async function cacheEvents(events) { - const db = await getDB(); - const tx = db.transaction('events', 'readwrite'); - for (const event of events) { - const cached = { - ...event, - cached_at: Date.now() - }; - await tx.store.put(cached); - } - await tx.done; -} -/** - * Get event by ID from cache - */ -export async function getEvent(id) { - const db = await getDB(); - return db.get('events', id); -} -/** - * Get events by kind - */ -export async function getEventsByKind(kind, limit) { - const db = await getDB(); - const tx = db.transaction('events', 'readonly'); - const index = tx.store.index('kind'); - const events = []; - let count = 0; - for await (const cursor of index.iterate(kind)) { - if (limit && count >= limit) - break; - events.push(cursor.value); - count++; - } - await tx.done; - return events.sort((a, b) => b.created_at - a.created_at); -} -/** - * Get events by pubkey - */ -export async function getEventsByPubkey(pubkey, limit) { - const db = await getDB(); - const tx = db.transaction('events', 'readonly'); - const index = tx.store.index('pubkey'); - const events = []; - let count = 0; - for await (const cursor of index.iterate(pubkey)) { - if (limit && count >= limit) - break; - events.push(cursor.value); - count++; - } - await tx.done; - return events.sort((a, b) => b.created_at - a.created_at); -} -/** - * Clear old events (older than specified timestamp) - */ -export async function clearOldEvents(olderThan) { - const db = await getDB(); - const tx = db.transaction('events', 'readwrite'); - const index = tx.store.index('created_at'); - for await (const cursor of index.iterate()) { - if (cursor.value.created_at < olderThan) { - await cursor.delete(); - } - } - await tx.done; -} -//# sourceMappingURL=event-cache.js.map \ No newline at end of file diff --git a/src/lib/services/cache/event-cache.js.map b/src/lib/services/cache/event-cache.js.map deleted file mode 100644 index f309be8..0000000 --- a/src/lib/services/cache/event-cache.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"event-cache.js","sourceRoot":"","sources":["event-cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAO7C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAiB;IAChD,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,MAAM,GAAgB;QAC1B,GAAG,KAAK;QACR,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;IACF,MAAM,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAoB;IACpD,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IACjD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAgB;YAC1B,GAAG,KAAK;YACR,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QACF,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IACD,MAAM,EAAE,CAAC,IAAI,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,EAAU;IACvC,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,OAAO,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY,EAAE,KAAc;IAChE,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/C,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK;YAAE,MAAM;QACnC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC1B,KAAK,EAAE,CAAC;IACV,CAAC;IAED,MAAM,EAAE,CAAC,IAAI,CAAC;IAEd,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;AAC5D,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAc,EAAE,KAAc;IACpE,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACjD,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK;YAAE,MAAM;QACnC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC1B,KAAK,EAAE,CAAC;IACV,CAAC;IAED,MAAM,EAAE,CAAC,IAAI,CAAC;IAEd,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;AAC5D,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,SAAiB;IACpD,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAE3C,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3C,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,GAAG,SAAS,EAAE,CAAC;YACxC,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;QACxB,CAAC;IACH,CAAC;IAED,MAAM,EAAE,CAAC,IAAI,CAAC;AAChB,CAAC"} \ No newline at end of file diff --git a/src/lib/services/cache/event-cache.ts b/src/lib/services/cache/event-cache.ts index da80e8a..cd1dd62 100644 --- a/src/lib/services/cache/event-cache.ts +++ b/src/lib/services/cache/event-cache.ts @@ -87,6 +87,26 @@ export async function getEventsByPubkey(pubkey: string, limit?: number): Promise return events.sort((a, b) => b.created_at - a.created_at); } +/** + * Delete an event by ID from cache + */ +export async function deleteEvent(id: string): Promise { + const db = await getDB(); + await db.delete('events', id); +} + +/** + * Delete multiple events by ID from cache + */ +export async function deleteEvents(ids: string[]): Promise { + const db = await getDB(); + const tx = db.transaction('events', 'readwrite'); + for (const id of ids) { + await tx.store.delete(id); + } + await tx.done; +} + /** * Clear old events (older than specified timestamp) */ diff --git a/src/lib/services/cache/indexeddb-store.js b/src/lib/services/cache/indexeddb-store.js deleted file mode 100644 index f56b98e..0000000 --- a/src/lib/services/cache/indexeddb-store.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Base IndexedDB store operations - */ -import { openDB } from 'idb'; -const DB_NAME = 'aitherboard'; -const DB_VERSION = 1; -let dbInstance = null; -/** - * Get or create database instance - */ -export async function getDB() { - if (dbInstance) - return dbInstance; - dbInstance = await openDB(DB_NAME, DB_VERSION, { - upgrade(db) { - // Events store - if (!db.objectStoreNames.contains('events')) { - const eventStore = db.createObjectStore('events', { keyPath: 'id' }); - eventStore.createIndex('kind', 'kind', { unique: false }); - eventStore.createIndex('pubkey', 'pubkey', { unique: false }); - eventStore.createIndex('created_at', 'created_at', { unique: false }); - } - // Profiles store - if (!db.objectStoreNames.contains('profiles')) { - db.createObjectStore('profiles', { keyPath: 'pubkey' }); - } - // Keys store - if (!db.objectStoreNames.contains('keys')) { - db.createObjectStore('keys', { keyPath: 'id' }); - } - // Search index store - if (!db.objectStoreNames.contains('search')) { - db.createObjectStore('search', { keyPath: 'id' }); - } - } - }); - return dbInstance; -} -/** - * Close database connection - */ -export async function closeDB() { - if (dbInstance) { - dbInstance.close(); - dbInstance = null; - } -} -//# sourceMappingURL=indexeddb-store.js.map \ No newline at end of file diff --git a/src/lib/services/cache/indexeddb-store.js.map b/src/lib/services/cache/indexeddb-store.js.map deleted file mode 100644 index b78ccb4..0000000 --- a/src/lib/services/cache/indexeddb-store.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"indexeddb-store.js","sourceRoot":"","sources":["indexeddb-store.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAqB,MAAM,KAAK,CAAC;AAEhD,MAAM,OAAO,GAAG,aAAa,CAAC;AAC9B,MAAM,UAAU,GAAG,CAAC,CAAC;AAsBrB,IAAI,UAAU,GAAwC,IAAI,CAAC;AAE3D;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IAElC,UAAU,GAAG,MAAM,MAAM,CAAiB,OAAO,EAAE,UAAU,EAAE;QAC7D,OAAO,CAAC,EAAE;YACR,eAAe;YACf,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5C,MAAM,UAAU,GAAG,EAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;gBACrE,UAAU,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC1D,UAAU,CAAC,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC9D,UAAU,CAAC,WAAW,CAAC,YAAY,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;YACxE,CAAC;YAED,iBAAiB;YACjB,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC9C,EAAE,CAAC,iBAAiB,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC1D,CAAC;YAED,aAAa;YACb,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC1C,EAAE,CAAC,iBAAiB,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YAClD,CAAC;YAED,qBAAqB;YACrB,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5C,EAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;KACF,CAAC,CAAC;IAEH,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO;IAC3B,IAAI,UAAU,EAAE,CAAC;QACf,UAAU,CAAC,KAAK,EAAE,CAAC;QACnB,UAAU,GAAG,IAAI,CAAC;IACpB,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/src/lib/services/cache/profile-cache.js b/src/lib/services/cache/profile-cache.js deleted file mode 100644 index fd4e510..0000000 --- a/src/lib/services/cache/profile-cache.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Profile caching (kind 0 events) - */ -import { getDB } from './indexeddb-store.js'; -/** - * Store a profile in cache - */ -export async function cacheProfile(event) { - if (event.kind !== 0) - throw new Error('Not a profile event'); - const db = await getDB(); - const cached = { - pubkey: event.pubkey, - event, - cached_at: Date.now() - }; - await db.put('profiles', cached); -} -/** - * Get profile by pubkey from cache - */ -export async function getProfile(pubkey) { - const db = await getDB(); - return db.get('profiles', pubkey); -} -/** - * Get multiple profiles - */ -export async function getProfiles(pubkeys) { - const db = await getDB(); - const profiles = new Map(); - const tx = db.transaction('profiles', 'readonly'); - for (const pubkey of pubkeys) { - const profile = await tx.store.get(pubkey); - if (profile) { - profiles.set(pubkey, profile); - } - } - await tx.done; - return profiles; -} -//# sourceMappingURL=profile-cache.js.map \ No newline at end of file diff --git a/src/lib/services/cache/profile-cache.js.map b/src/lib/services/cache/profile-cache.js.map deleted file mode 100644 index ebe2627..0000000 --- a/src/lib/services/cache/profile-cache.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"profile-cache.js","sourceRoot":"","sources":["profile-cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAS7C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAiB;IAClD,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAC7D,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,MAAM,GAAkB;QAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,KAAK;QACL,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;IACF,MAAM,EAAE,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAc;IAC7C,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,OAAO,EAAE,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAiB;IACjD,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;IAClD,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAElD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,OAAO,EAAE,CAAC;YACZ,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,MAAM,EAAE,CAAC,IAAI,CAAC;IACd,OAAO,QAAQ,CAAC;AAClB,CAAC"} \ No newline at end of file diff --git a/src/lib/services/cache/search-index.js b/src/lib/services/cache/search-index.js deleted file mode 100644 index c7e491f..0000000 --- a/src/lib/services/cache/search-index.js +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Full-text search index (deferred implementation) - */ -import { getDB } from './indexeddb-store.js'; -/** - * Index event content for search - */ -export async function indexEvent(eventId, content) { - // Placeholder - full implementation would: - // 1. Tokenize content - // 2. Create inverted index - // 3. Store in IndexedDB - const db = await getDB(); - await db.put('search', { - id: eventId, - content: content.toLowerCase() - }); -} -/** - * Search events by query - */ -export async function searchEvents(query, limit = 50) { - // Placeholder - full implementation would: - // 1. Tokenize query - // 2. Look up in inverted index - // 3. Rank results - // 4. Return event IDs - const db = await getDB(); - const results = []; - const lowerQuery = query.toLowerCase(); - const tx = db.transaction('search', 'readonly'); - for await (const cursor of tx.store.iterate()) { - if (results.length >= limit) - break; - const content = cursor.value.content; - if (content.includes(lowerQuery)) { - results.push(cursor.key); - } - } - await tx.done; - return results; -} -//# sourceMappingURL=search-index.js.map \ No newline at end of file diff --git a/src/lib/services/cache/search-index.js.map b/src/lib/services/cache/search-index.js.map deleted file mode 100644 index 386afb7..0000000 --- a/src/lib/services/cache/search-index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"search-index.js","sourceRoot":"","sources":["search-index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAE7C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,OAAe;IAC/D,2CAA2C;IAC3C,sBAAsB;IACtB,2BAA2B;IAC3B,wBAAwB;IACxB,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE;QACrB,EAAE,EAAE,OAAO;QACX,OAAO,EAAE,OAAO,CAAC,WAAW,EAAE;KAC/B,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAa,EAAE,QAAgB,EAAE;IAClE,2CAA2C;IAC3C,oBAAoB;IACpB,+BAA+B;IAC/B,kBAAkB;IAClB,sBAAsB;IACtB,MAAM,EAAE,GAAG,MAAM,KAAK,EAAE,CAAC;IACzB,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IACvC,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAEhD,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAC9C,IAAI,OAAO,CAAC,MAAM,IAAI,KAAK;YAAE,MAAM;QACnC,MAAM,OAAO,GAAI,MAAM,CAAC,KAA6B,CAAC,OAAO,CAAC;QAC9D,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAa,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,MAAM,EAAE,CAAC,IAAI,CAAC;IACd,OAAO,OAAO,CAAC;AACjB,CAAC"} \ No newline at end of file diff --git a/src/lib/services/nostr/applesauce-client.js b/src/lib/services/nostr/applesauce-client.js deleted file mode 100644 index b1094ac..0000000 --- a/src/lib/services/nostr/applesauce-client.js +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Applesauce-core client wrapper - * Main interface for Nostr operations - */ -import { initializeRelayPool, relayPool } from './relay-pool.js'; -import { subscriptionManager } from './subscription-manager.js'; -import { eventStore } from './event-store.js'; -import { config } from './config.js'; -class ApplesauceClient { - initialized = false; - /** - * Initialize the client - */ - async initialize() { - if (this.initialized) - return; - await initializeRelayPool(); - this.initialized = true; - } - /** - * Publish an event to relays - */ - async publish(event, options = {}) { - const relays = options.relays || relayPool.getConnectedRelays(); - const message = JSON.stringify(['EVENT', event]); - const results = { - success: [], - failed: [] - }; - for (const relay of relays) { - try { - const sent = relayPool.send(relay, message); - if (sent) { - results.success.push(relay); - } - else { - results.failed.push({ relay, error: 'Not connected' }); - } - } - catch (error) { - results.failed.push({ - relay, - error: error instanceof Error ? error.message : 'Unknown error' - }); - } - } - // Store in cache - if (results.success.length > 0) { - await eventStore.storeEvent(event); - } - return results; - } - /** - * Subscribe to events - */ - subscribe(filters, relays, onEvent, onEose) { - const subId = subscriptionManager.generateSubId(); - subscriptionManager.subscribe(subId, relays, filters, onEvent, onEose); - return subId; - } - /** - * Unsubscribe - */ - unsubscribe(subId) { - subscriptionManager.unsubscribe(subId); - } - /** - * Fetch events - */ - async fetchEvents(filters, relays, options) { - return eventStore.fetchEvents(filters, relays, options || {}); - } - /** - * Get event by ID - */ - async getEventById(id, relays) { - return eventStore.getEventById(id, relays); - } - /** - * Get relay pool - */ - getRelayPool() { - return relayPool; - } - /** - * Get config - */ - getConfig() { - return config; - } - /** - * Close all connections - */ - close() { - subscriptionManager.closeAll(); - relayPool.closeAll(); - this.initialized = false; - } -} -export const nostrClient = new ApplesauceClient(); -//# sourceMappingURL=applesauce-client.js.map \ No newline at end of file diff --git a/src/lib/services/nostr/applesauce-client.js.map b/src/lib/services/nostr/applesauce-client.js.map deleted file mode 100644 index 5b3535e..0000000 --- a/src/lib/services/nostr/applesauce-client.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"applesauce-client.js","sourceRoot":"","sources":["applesauce-client.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/lib/services/nostr/applesauce-client.ts b/src/lib/services/nostr/applesauce-client.ts index 823b8b0..a3c72bf 100644 --- a/src/lib/services/nostr/applesauce-client.ts +++ b/src/lib/services/nostr/applesauce-client.ts @@ -5,7 +5,7 @@ import { initializeRelayPool, relayPool } from './relay-pool.js'; import { subscriptionManager } from './subscription-manager.js'; -import { eventStore } from './event-store.js'; +import { eventStore, warmupCaches } from './event-store.js'; import { config } from './config.js'; import type { NostrEvent } from '../../types/nostr.js'; @@ -25,6 +25,9 @@ class ApplesauceClient { await initializeRelayPool(); this.initialized = true; + + // Warm up caches in background (non-blocking) + warmupCaches(); } /** @@ -111,7 +114,7 @@ class ApplesauceClient { limit?: number; }>, relays: string[], - options?: { useCache?: boolean; cacheResults?: boolean } + options?: { useCache?: boolean; cacheResults?: boolean; onUpdate?: (events: NostrEvent[]) => void } ): Promise { return eventStore.fetchEvents(filters, relays, options || {}); } diff --git a/src/lib/services/nostr/auth-handler.js b/src/lib/services/nostr/auth-handler.js deleted file mode 100644 index 51b1762..0000000 --- a/src/lib/services/nostr/auth-handler.js +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Unified authentication handler - */ -import { signEventWithNIP07, getPublicKeyWithNIP07 } from '../auth/nip07-signer.js'; -import { signEventWithNsec, getPublicKeyFromNsec } from '../auth/nsec-signer.js'; -import { signEventWithBunker, connectBunker } from '../auth/bunker-signer.js'; -import { signEventWithAnonymous, generateAnonymousKey } from '../auth/anonymous-signer.js'; -import { decryptPrivateKey } from '../security/key-management.js'; -import { sessionManager } from '../auth/session-manager.js'; -import { fetchRelayLists } from '../auth/relay-list-fetcher.js'; -import { eventStore } from './event-store.js'; -import { nostrClient } from './applesauce-client.js'; -/** - * Authenticate with NIP-07 - */ -export async function authenticateWithNIP07() { - const pubkey = await getPublicKeyWithNIP07(); - sessionManager.setSession({ - pubkey, - method: 'nip07', - signer: signEventWithNIP07, - createdAt: Date.now() - }); - // Fetch user relay lists and mute list - await loadUserPreferences(pubkey); - return pubkey; -} -/** - * Authenticate with nsec - */ -export async function authenticateWithNsec(ncryptsec, password) { - // Decrypt the encrypted private key - const nsec = await decryptPrivateKey(ncryptsec, password); - // Derive public key from private key - const pubkey = await getPublicKeyFromNsec(nsec); - sessionManager.setSession({ - pubkey, - method: 'nsec', - signer: async (event) => signEventWithNsec(event, ncryptsec, password), - createdAt: Date.now() - }); - await loadUserPreferences(pubkey); - return pubkey; -} -/** - * Authenticate with bunker - */ -export async function authenticateWithBunker(bunkerUri) { - const connection = await connectBunker(bunkerUri); - sessionManager.setSession({ - pubkey: connection.pubkey, - method: 'bunker', - signer: async (event) => signEventWithBunker(event, connection), - createdAt: Date.now() - }); - await loadUserPreferences(connection.pubkey); - return connection.pubkey; -} -/** - * Authenticate as anonymous - */ -export async function authenticateAsAnonymous(password) { - const { pubkey, nsec } = await generateAnonymousKey(password); - // Store the key for later use - // In practice, we'd need to store the ncryptsec and decrypt when needed - // For now, this is simplified - sessionManager.setSession({ - pubkey, - method: 'anonymous', - signer: async (event) => { - // Simplified - would decrypt and sign - return signEventWithAnonymous(event, pubkey, password); - }, - createdAt: Date.now() - }); - return pubkey; -} -/** - * Load user preferences (relay lists, mute list, blocked relays) - */ -async function loadUserPreferences(pubkey) { - // Fetch relay lists - const { inbox, outbox } = await fetchRelayLists(pubkey); - // Relay lists would be used by relay selection logic - // Fetch mute list (kind 10000) - const muteEvents = await nostrClient.fetchEvents([{ kinds: [10000], authors: [pubkey], limit: 1 }], [...nostrClient.getConfig().defaultRelays, ...nostrClient.getConfig().profileRelays], { useCache: true, cacheResults: true }); - if (muteEvents.length > 0) { - const mutedPubkeys = muteEvents[0].tags - .filter((t) => t[0] === 'p') - .map((t) => t[1]) - .filter(Boolean); - eventStore.setMuteList(mutedPubkeys); - } - // Fetch blocked relays (kind 10006) - const blockedRelayEvents = await nostrClient.fetchEvents([{ kinds: [10006], authors: [pubkey], limit: 1 }], [...nostrClient.getConfig().defaultRelays, ...nostrClient.getConfig().profileRelays], { useCache: true, cacheResults: true }); - if (blockedRelayEvents.length > 0) { - const blockedRelays = blockedRelayEvents[0].tags - .filter((t) => t[0] === 'relay') - .map((t) => t[1]) - .filter(Boolean); - eventStore.setBlockedRelays(blockedRelays); - } -} -/** - * Sign and publish event - */ -export async function signAndPublish(event, relays) { - const signed = await sessionManager.signEvent(event); - return nostrClient.publish(signed, { relays }); -} -/** - * Logout - */ -export function logout() { - sessionManager.clearSession(); - eventStore.setMuteList([]); - eventStore.setBlockedRelays([]); -} -//# sourceMappingURL=auth-handler.js.map \ No newline at end of file diff --git a/src/lib/services/nostr/auth-handler.js.map b/src/lib/services/nostr/auth-handler.js.map deleted file mode 100644 index 84275c8..0000000 --- a/src/lib/services/nostr/auth-handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"auth-handler.js","sourceRoot":"","sources":["auth-handler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/lib/services/nostr/auth-handler.ts b/src/lib/services/nostr/auth-handler.ts index 9e669b1..af202fc 100644 --- a/src/lib/services/nostr/auth-handler.ts +++ b/src/lib/services/nostr/auth-handler.ts @@ -27,7 +27,7 @@ export async function authenticateWithNIP07(): Promise { method: 'nip07', signer: signEventWithNIP07, createdAt: Date.now() - }); + }, {}); // No metadata needed for NIP-07 // Fetch user relay lists and mute list await loadUserPreferences(pubkey); @@ -71,7 +71,7 @@ export async function authenticateWithBunker(bunkerUri: string): Promise method: 'bunker', signer: async (event) => signEventWithBunker(event, connection), createdAt: Date.now() - }); + }, { bunkerUri }); // Store bunker URI for restoration await loadUserPreferences(connection.pubkey); diff --git a/src/lib/services/nostr/config.js b/src/lib/services/nostr/config.js deleted file mode 100644 index d3bcce7..0000000 --- a/src/lib/services/nostr/config.js +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Configuration for Nostr services - * Handles environment variables and defaults - */ -const DEFAULT_RELAYS = [ - 'wss://theforest.nostr1.com', - 'wss://nostr21.com', - 'wss://nostr.land', - 'wss://nostr.wine', - 'wss://nostr.sovbit.host' -]; -const PROFILE_RELAYS = [ - 'wss://relay.damus.io', - 'wss://aggr.nostr.land', - 'wss://profiles.nostr1.com' -]; -function parseRelays(envVar, fallback) { - if (!envVar) - return fallback; - const relays = envVar - .split(',') - .map((r) => r.trim()) - .filter((r) => r.length > 0); - return relays.length > 0 ? relays : fallback; -} -function parseIntEnv(envVar, fallback, min = 0) { - if (!envVar) - return fallback; - const parsed = parseInt(envVar, 10); - if (isNaN(parsed) || parsed < min) - return fallback; - return parsed; -} -function parseBoolEnv(envVar, fallback) { - if (!envVar) - return fallback; - return envVar.toLowerCase() === 'true' || envVar === '1'; -} -export function getConfig() { - return { - defaultRelays: parseRelays(import.meta.env.VITE_DEFAULT_RELAYS, DEFAULT_RELAYS), - profileRelays: PROFILE_RELAYS, - zapThreshold: parseIntEnv(import.meta.env.VITE_ZAP_THRESHOLD, 1, 0), - threadTimeoutDays: parseIntEnv(import.meta.env.VITE_THREAD_TIMEOUT_DAYS, 30), - pwaEnabled: parseBoolEnv(import.meta.env.VITE_PWA_ENABLED, true) - }; -} -export const config = getConfig(); -//# sourceMappingURL=config.js.map \ No newline at end of file diff --git a/src/lib/services/nostr/config.js.map b/src/lib/services/nostr/config.js.map deleted file mode 100644 index 8d044b5..0000000 --- a/src/lib/services/nostr/config.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"config.js","sourceRoot":"","sources":["config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,cAAc,GAAG;IACrB,4BAA4B;IAC5B,mBAAmB;IACnB,kBAAkB;IAClB,kBAAkB;IAClB,yBAAyB;CAC1B,CAAC;AAEF,MAAM,cAAc,GAAG;IACrB,sBAAsB;IACtB,uBAAuB;IACvB,2BAA2B;CAC5B,CAAC;AAUF,SAAS,WAAW,CAAC,MAA0B,EAAE,QAAkB;IACjE,IAAI,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAC7B,MAAM,MAAM,GAAG,MAAM;SAClB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/B,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC/C,CAAC;AAED,SAAS,WAAW,CAAC,MAA0B,EAAE,QAAgB,EAAE,MAAc,CAAC;IAChF,IAAI,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAC7B,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACpC,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,GAAG;QAAE,OAAO,QAAQ,CAAC;IACnD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,YAAY,CAAC,MAA0B,EAAE,QAAiB;IACjE,IAAI,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAC7B,OAAO,MAAM,CAAC,WAAW,EAAE,KAAK,MAAM,IAAI,MAAM,KAAK,GAAG,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO;QACL,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,cAAc,CAAC;QAC/E,aAAa,EAAE,cAAc;QAC7B,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC,EAAE,CAAC,CAAC;QACnE,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,wBAAwB,EAAE,EAAE,CAAC;QAC5E,UAAU,EAAE,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,CAAC;KACjE,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC"} \ No newline at end of file diff --git a/src/lib/services/nostr/event-store.js b/src/lib/services/nostr/event-store.js deleted file mode 100644 index 0079828..0000000 --- a/src/lib/services/nostr/event-store.js +++ /dev/null @@ -1,213 +0,0 @@ -/** - * Event store with IndexedDB caching and filtering - */ -import { cacheEvent, cacheEvents, getEvent, getEventsByKind, getEventsByPubkey } from '../cache/event-cache.js'; -import { subscriptionManager } from './subscription-manager.js'; -class EventStore { - muteList = new Set(); - blockedRelays = new Set(); - activityTracker = new Map(); // pubkey -> last activity timestamp - /** - * Update mute list - */ - setMuteList(pubkeys) { - this.muteList = new Set(pubkeys); - } - /** - * Update blocked relays - */ - setBlockedRelays(relays) { - this.blockedRelays = new Set(relays); - } - /** - * Filter out muted events - */ - isMuted(event) { - return this.muteList.has(event.pubkey); - } - /** - * Filter out blocked relays - */ - filterBlockedRelays(relays) { - return relays.filter((r) => !this.blockedRelays.has(r)); - } - /** - * Track activity for a pubkey - */ - trackActivity(pubkey, timestamp) { - const current = this.activityTracker.get(pubkey) || 0; - if (timestamp > current) { - this.activityTracker.set(pubkey, timestamp); - } - } - /** - * Get last activity timestamp for a pubkey - */ - getLastActivity(pubkey) { - return this.activityTracker.get(pubkey); - } - /** - * Check if event should be hidden (content filtering) - */ - shouldHideEvent(event) { - // Check for content-warning or sensitive tags - const hasContentWarning = event.tags.some((t) => t[0] === 'content-warning' || t[0] === 'sensitive'); - if (hasContentWarning) - return true; - // Check for #NSFW in content or tags - const content = event.content.toLowerCase(); - const hasNSFW = content.includes('#nsfw') || event.tags.some((t) => t[1]?.toLowerCase() === 'nsfw'); - if (hasNSFW) - return true; - return false; - } - /** - * Fetch events with filters - */ - async fetchEvents(filters, relays, options = {}) { - const { useCache = true, cacheResults = true } = options; - // Filter out blocked relays - const filteredRelays = this.filterBlockedRelays(relays); - // Try cache first if enabled - if (useCache) { - // Simple cache lookup - could be improved - const cachedEvents = []; - for (const filter of filters) { - if (filter.kinds && filter.kinds.length === 1) { - const events = await getEventsByKind(filter.kinds[0], filter.limit); - cachedEvents.push(...events); - } - if (filter.authors && filter.authors.length === 1) { - const events = await getEventsByPubkey(filter.authors[0], filter.limit); - cachedEvents.push(...events); - } - } - if (cachedEvents.length > 0) { - // Return cached events immediately (progressive loading) - // Continue fetching fresh data in background - this.fetchEventsFromRelays(filters, filteredRelays, { cacheResults }).catch((error) => { - console.error('Error fetching fresh events from relays:', error); - }); - return this.filterEvents(cachedEvents); - } - } - // Fetch from relays - return this.fetchEventsFromRelays(filters, filteredRelays, { cacheResults }); - } - /** - * Fetch events from relays - */ - async fetchEventsFromRelays(filters, relays, options) { - return new Promise((resolve, reject) => { - const events = new Map(); - const subId = subscriptionManager.generateSubId(); - const relayCount = new Set(); - let resolved = false; - let eoseTimeout = null; - let timeoutId = null; - const finish = (eventArray) => { - if (resolved) - return; - resolved = true; - // Clean up timeouts - if (timeoutId) { - clearTimeout(timeoutId); - timeoutId = null; - } - if (eoseTimeout) { - clearTimeout(eoseTimeout); - eoseTimeout = null; - } - subscriptionManager.unsubscribe(subId); - resolve(this.filterEvents(eventArray)); - }; - const onEvent = (event, relay) => { - // Skip muted events - if (this.isMuted(event)) - return; - // Skip hidden events - if (this.shouldHideEvent(event)) - return; - // Track activity - this.trackActivity(event.pubkey, event.created_at); - // Deduplicate by event ID - events.set(event.id, event); - relayCount.add(relay); - }; - const onEose = (relay) => { - relayCount.add(relay); - // Wait a bit for all relays to respond - if (eoseTimeout) { - clearTimeout(eoseTimeout); - } - eoseTimeout = setTimeout(() => { - if (!resolved && relayCount.size >= Math.min(relays.length, 3)) { - // Got responses from enough relays - const eventArray = Array.from(events.values()); - if (options.cacheResults) { - cacheEvents(eventArray).catch((error) => { - console.error('Error caching events:', error); - }); - } - finish(eventArray); - } - }, 1000); - }; - try { - subscriptionManager.subscribe(subId, relays, filters, onEvent, onEose); - } - catch (error) { - reject(error); - return; - } - // Timeout after 10 seconds - timeoutId = setTimeout(() => { - if (!resolved) { - const eventArray = Array.from(events.values()); - if (options.cacheResults) { - cacheEvents(eventArray).catch((error) => { - console.error('Error caching events:', error); - }); - } - finish(eventArray); - } - }, 10000); - }); - } - /** - * Filter events (remove muted, hidden, etc.) - */ - filterEvents(events) { - return events.filter((event) => { - if (this.isMuted(event)) - return false; - if (this.shouldHideEvent(event)) - return false; - return true; - }); - } - /** - * Get event by ID (from cache or fetch) - */ - async getEventById(id, relays) { - // Try cache first - const cached = await getEvent(id); - if (cached) - return cached; - // Fetch from relays - const filters = [{ ids: [id] }]; - const events = await this.fetchEvents(filters, relays, { useCache: false }); - return events[0] || null; - } - /** - * Store event in cache - */ - async storeEvent(event) { - if (this.isMuted(event) || this.shouldHideEvent(event)) - return; - this.trackActivity(event.pubkey, event.created_at); - await cacheEvent(event); - } -} -export const eventStore = new EventStore(); -//# sourceMappingURL=event-store.js.map \ No newline at end of file diff --git a/src/lib/services/nostr/event-store.js.map b/src/lib/services/nostr/event-store.js.map deleted file mode 100644 index f3103ea..0000000 --- a/src/lib/services/nostr/event-store.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"event-store.js","sourceRoot":"","sources":["event-store.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/lib/services/nostr/event-store.ts b/src/lib/services/nostr/event-store.ts index 2b7a243..04cdc13 100644 --- a/src/lib/services/nostr/event-store.ts +++ b/src/lib/services/nostr/event-store.ts @@ -2,7 +2,7 @@ * Event store with IndexedDB caching and filtering */ -import { cacheEvent, cacheEvents, getEvent, getEventsByKind, getEventsByPubkey } from '../cache/event-cache.js'; +import { cacheEvent, cacheEvents, getEvent, getEventsByKind, getEventsByPubkey, deleteEvents } from '../cache/event-cache.js'; import { subscriptionManager } from './subscription-manager.js'; import { relayPool } from './relay-pool.js'; import type { NostrEvent, NostrFilter } from '../../types/nostr.js'; @@ -16,6 +16,8 @@ class EventStore { private muteList: Set = new Set(); private blockedRelays: Set = new Set(); private activityTracker: Map = new Map(); // pubkey -> last activity timestamp + private initialized = false; + private deletionProcessorInterval: ReturnType | null = null; /** * Update mute list @@ -55,11 +57,58 @@ class EventStore { } } + /** + * Initialize activity tracker from cached events + */ + private async initializeActivityTracker(): Promise { + if (this.initialized) return; + this.initialized = true; + + try { + // Load recent events from cache to populate activity tracker + // We'll check the most recent events for each pubkey + // Include all event kinds that indicate user activity + const recentEvents = await getEventsByKind(1, 1000); // Get recent kind 1 events (notes) + const threadEvents = await getEventsByKind(11, 1000); // Get recent thread events + const commentEvents = await getEventsByKind(1111, 1000); // Get recent comment events + const reactionEvents = await getEventsByKind(7, 1000).catch(() => []); // Get recent reactions (kind 7) + const profileEvents = await getEventsByKind(0, 1000).catch(() => []); // Get recent profile updates (kind 0) + + const allEvents = [...recentEvents, ...threadEvents, ...commentEvents, ...reactionEvents, ...profileEvents]; + + // Update activity tracker with the most recent event per pubkey + for (const event of allEvents) { + this.trackActivity(event.pubkey, event.created_at); + } + } catch (error) { + console.error('Error initializing activity tracker:', error); + } + } + /** * Get last activity timestamp for a pubkey */ - getLastActivity(pubkey: string): number | undefined { - return this.activityTracker.get(pubkey); + async getLastActivity(pubkey: string): Promise { + // Initialize from cache if not done yet + await this.initializeActivityTracker(); + + // Check in-memory tracker first + const inMemory = this.activityTracker.get(pubkey); + if (inMemory) return inMemory; + + // If not in memory, check cache for this specific pubkey + try { + const events = await getEventsByPubkey(pubkey, 1); // Get most recent event + if (events.length > 0) { + const latestEvent = events[0]; + this.trackActivity(pubkey, latestEvent.created_at); + return latestEvent.created_at; + } + } catch (error) { + console.error('Error fetching activity from cache:', error); + } + + return undefined; } /** @@ -80,44 +129,57 @@ class EventStore { /** * Fetch events with filters + * Returns cached data immediately, then fetches from relays in background */ async fetchEvents( filters: NostrFilter[], relays: string[], - options: { useCache?: boolean; cacheResults?: boolean } = {} + options: { useCache?: boolean; cacheResults?: boolean; onUpdate?: (events: NostrEvent[]) => void } = {} ): Promise { - const { useCache = true, cacheResults = true } = options; + const { useCache = true, cacheResults = true, onUpdate } = options; // Filter out blocked relays const filteredRelays = this.filterBlockedRelays(relays); - // Try cache first if enabled + // Try cache first if enabled - return immediately if (useCache) { - // Simple cache lookup - could be improved const cachedEvents: NostrEvent[] = []; for (const filter of filters) { if (filter.kinds && filter.kinds.length === 1) { - const events = await getEventsByKind(filter.kinds[0], filter.limit); + const events = await getEventsByKind(filter.kinds[0], filter.limit || 50); cachedEvents.push(...events); } if (filter.authors && filter.authors.length === 1) { - const events = await getEventsByPubkey(filter.authors[0], filter.limit); + const events = await getEventsByPubkey(filter.authors[0], filter.limit || 50); cachedEvents.push(...events); } + // Handle multiple kinds + if (filter.kinds && filter.kinds.length > 1) { + for (const kind of filter.kinds) { + const events = await getEventsByKind(kind, filter.limit || 50); + cachedEvents.push(...events); + } + } } - if (cachedEvents.length > 0) { - // Return cached events immediately (progressive loading) - // Continue fetching fresh data in background - this.fetchEventsFromRelays(filters, filteredRelays, { cacheResults }).catch((error) => { - console.error('Error fetching fresh events from relays:', error); - }); - return this.filterEvents(cachedEvents); + // Return cached events immediately (non-blocking) + const filteredCached = this.filterEvents(cachedEvents); + + // Fetch fresh data from relays in background (non-blocking) + if (cacheResults) { + // Use setTimeout to ensure this doesn't block the return + setTimeout(() => { + this.fetchEventsFromRelays(filters, filteredRelays, { cacheResults, onUpdate }).catch((error) => { + console.error('Error fetching fresh events from relays:', error); + }); + }, 0); } + + return filteredCached; } - // Fetch from relays - return this.fetchEventsFromRelays(filters, filteredRelays, { cacheResults }); + // No cache - fetch from relays + return this.fetchEventsFromRelays(filters, filteredRelays, { cacheResults, onUpdate }); } /** @@ -126,7 +188,7 @@ class EventStore { private async fetchEventsFromRelays( filters: NostrFilter[], relays: string[], - options: { cacheResults: boolean } + options: { cacheResults: boolean; onUpdate?: (events: NostrEvent[]) => void } ): Promise { return new Promise((resolve, reject) => { const events: Map = new Map(); @@ -151,7 +213,14 @@ class EventStore { } subscriptionManager.unsubscribe(subId); - resolve(this.filterEvents(eventArray)); + const filtered = this.filterEvents(eventArray); + + // Notify callback if provided (for reactive UI updates) + if (options.onUpdate) { + options.onUpdate(filtered); + } + + resolve(filtered); }; const onEvent = (event: NostrEvent, relay: string) => { @@ -161,6 +230,13 @@ class EventStore { // Skip hidden events if (this.shouldHideEvent(event)) return; + // Process kind 5 deletion events immediately when received + if (event.kind === 5) { + this.processDeletionEvent(event).catch((error) => { + console.error('Error processing deletion event:', error); + }); + } + // Track activity this.trackActivity(event.pubkey, event.created_at); @@ -241,9 +317,164 @@ class EventStore { */ async storeEvent(event: NostrEvent): Promise { if (this.isMuted(event) || this.shouldHideEvent(event)) return; + + // Process kind 5 deletion events immediately + if (event.kind === 5) { + await this.processDeletionEvent(event); + // Also cache the deletion event itself + this.trackActivity(event.pubkey, event.created_at); + await cacheEvent(event); + return; + } + this.trackActivity(event.pubkey, event.created_at); await cacheEvent(event); } + + /** + * Process a kind 5 deletion event (NIP-09) + * Deletes events referenced in the 'e' tags that belong to the same author + */ + private async processDeletionEvent(deletionEvent: NostrEvent): Promise { + if (deletionEvent.kind !== 5) return; + + const authorPubkey = deletionEvent.pubkey; + const eventIdsToDelete: string[] = []; + + // Extract event IDs from 'e' tags + for (const tag of deletionEvent.tags) { + if (tag[0] === 'e' && tag[1]) { + eventIdsToDelete.push(tag[1]); + } + } + + if (eventIdsToDelete.length === 0) return; + + // Verify that the events to delete belong to the same author + // This is a security measure - only delete events from the same pubkey + const eventsToVerify = await Promise.all( + eventIdsToDelete.map(id => getEvent(id)) + ); + + const verifiedIds: string[] = []; + for (let i = 0; i < eventIdsToDelete.length; i++) { + const event = eventsToVerify[i]; + if (event && event.pubkey === authorPubkey) { + verifiedIds.push(eventIdsToDelete[i]); + } + } + + if (verifiedIds.length > 0) { + await deleteEvents(verifiedIds); + console.log(`Deleted ${verifiedIds.length} event(s) per NIP-09 deletion request from ${authorPubkey.slice(0, 16)}...`); + } + } + + /** + * Process all kind 5 deletion events in the cache + * This should be run periodically to clean up deleted events + */ + async processAllDeletionEvents(): Promise { + try { + // Get all kind 5 events from cache + const deletionEvents = await getEventsByKind(5, 1000); + + if (deletionEvents.length === 0) return; + + // Process each deletion event + for (const deletionEvent of deletionEvents) { + await this.processDeletionEvent(deletionEvent); + } + + console.log(`Processed ${deletionEvents.length} deletion event(s)`); + } catch (error) { + console.error('Error processing deletion events:', error); + } + } + + /** + * Start the background deletion processor + * Runs on startup and every 15 minutes + */ + startDeletionProcessor(): void { + // Process immediately on startup + this.processAllDeletionEvents().catch((error) => { + console.error('Error in initial deletion processing:', error); + }); + + // Then run every 15 minutes (900000 ms) + if (this.deletionProcessorInterval) { + clearInterval(this.deletionProcessorInterval); + } + + this.deletionProcessorInterval = setInterval(() => { + this.processAllDeletionEvents().catch((error) => { + console.error('Error in periodic deletion processing:', error); + }); + }, 15 * 60 * 1000); // 15 minutes + } + + /** + * Stop the background deletion processor + */ + stopDeletionProcessor(): void { + if (this.deletionProcessorInterval) { + clearInterval(this.deletionProcessorInterval); + this.deletionProcessorInterval = null; + } + } } export const eventStore = new EventStore(); + +/** + * Warm up caches on app initialization + * This runs in the background and doesn't block the UI + */ +export async function warmupCaches(): Promise { + // Run in background - don't await, just start the process + setTimeout(async () => { + try { + // Initialize activity tracker (loads from cache) + await eventStore.getLastActivity('dummy').catch(() => {}); // This triggers initialization + + // Pre-warm common event types + const config = (await import('./config.js')).config; + const defaultRelays = config.defaultRelays; + + // Warm up threads (kind 11) + eventStore.fetchEvents( + [{ kinds: [11], limit: 50 }], + defaultRelays, + { useCache: true, cacheResults: true } + ).catch((error) => { + console.error('Error warming thread cache:', error); + }); + + // Warm up notes (kind 1) + eventStore.fetchEvents( + [{ kinds: [1], limit: 100 }], + defaultRelays, + { useCache: true, cacheResults: true } + ).catch((error) => { + console.error('Error warming notes cache:', error); + }); + + // Warm up comments (kind 1111) + eventStore.fetchEvents( + [{ kinds: [1111], limit: 100 }], + defaultRelays, + { useCache: true, cacheResults: true } + ).catch((error) => { + console.error('Error warming comments cache:', error); + }); + + // Start the deletion processor (runs on startup and every 15 minutes) + eventStore.startDeletionProcessor(); + + console.log('Cache warming started in background'); + } catch (error) { + console.error('Error during cache warmup:', error); + } + }, 100); // Small delay to not block initial render +} diff --git a/src/lib/services/nostr/event-utils.js b/src/lib/services/nostr/event-utils.js deleted file mode 100644 index 64b6b01..0000000 --- a/src/lib/services/nostr/event-utils.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Event utilities for creating and signing events - */ -/** - * Create event ID (SHA256 of serialized event) - */ -export async function createEventId(event) { - const serialized = JSON.stringify([ - 0, - event.pubkey, - event.created_at, - event.kind, - event.tags, - event.content - ]); - const encoder = new TextEncoder(); - const data = encoder.encode(serialized); - const hashBuffer = await crypto.subtle.digest('SHA-256', data); - const hashArray = Array.from(new Uint8Array(hashBuffer)); - return hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); -} -/** - * Sign event (placeholder) - * - * TEMPORARY: Generates a deterministic signature-like string. - * This is NOT a valid secp256k1 signature but has the correct length. - * Production code MUST compute actual secp256k1 signature. - */ -export async function signEvent(event) { - const id = await createEventId(event); - // TEMPORARY: Generate deterministic signature-like string (128 chars) - const encoder = new TextEncoder(); - const sigData = encoder.encode(event.pubkey + id); - const sigHashBuffer = await crypto.subtle.digest('SHA-256', sigData); - const sigHashArray = Array.from(new Uint8Array(sigHashBuffer)); - const sigHash = sigHashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); - // Double the hash to get 128 chars (64 * 2) - const sig = (sigHash + sigHash).slice(0, 128); - return { ...event, id, sig }; -} -//# sourceMappingURL=event-utils.js.map \ No newline at end of file diff --git a/src/lib/services/nostr/event-utils.js.map b/src/lib/services/nostr/event-utils.js.map deleted file mode 100644 index 72c0554..0000000 --- a/src/lib/services/nostr/event-utils.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"event-utils.js","sourceRoot":"","sources":["event-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAqC;IACvE,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC;QAChC,CAAC;QACD,KAAK,CAAC,MAAM;QACZ,KAAK,CAAC,UAAU;QAChB,KAAK,CAAC,IAAI;QACV,KAAK,CAAC,IAAI;QACV,KAAK,CAAC,OAAO;KACd,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;IACzD,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,KAAqC;IAErC,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;IACtC,sEAAsE;IACtE,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAClD,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACrE,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClF,4CAA4C;IAC5C,MAAM,GAAG,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC9C,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;AAC/B,CAAC"} \ No newline at end of file diff --git a/src/lib/services/nostr/relay-pool.js b/src/lib/services/nostr/relay-pool.js deleted file mode 100644 index 4a662fe..0000000 --- a/src/lib/services/nostr/relay-pool.js +++ /dev/null @@ -1,181 +0,0 @@ -/** - * Relay pool management - * Manages WebSocket connections to Nostr relays - */ -import { config } from './config.js'; -class RelayPool { - relays = new Map(); - status = new Map(); - statusCallbacks = new Set(); - reconnectTimeouts = new Map(); - /** - * Add relay to pool - */ - async addRelay(url) { - if (this.relays.has(url)) - return; - this.relays.set(url, null); - this.updateStatus(url, { connected: false }); - await this.connect(url); - } - /** - * Remove relay from pool - */ - removeRelay(url) { - const ws = this.relays.get(url); - if (ws) { - ws.close(); - } - this.relays.delete(url); - this.status.delete(url); - const timeout = this.reconnectTimeouts.get(url); - if (timeout) { - clearTimeout(timeout); - this.reconnectTimeouts.delete(url); - } - } - /** - * Connect to a relay - */ - async connect(url) { - try { - const ws = new WebSocket(url); - const startTime = Date.now(); - ws.onopen = () => { - const latency = Date.now() - startTime; - this.relays.set(url, ws); - this.updateStatus(url, { - connected: true, - latency, - lastConnected: Date.now() - }); - }; - ws.onerror = (error) => { - this.updateStatus(url, { - connected: false, - lastError: error.message || 'Connection error' - }); - this.scheduleReconnect(url); - }; - ws.onclose = () => { - this.relays.set(url, null); - this.updateStatus(url, { connected: false }); - this.scheduleReconnect(url); - }; - // Store WebSocket for message sending - this.relays.set(url, ws); - } - catch (error) { - this.updateStatus(url, { - connected: false, - lastError: error instanceof Error ? error.message : 'Unknown error' - }); - this.scheduleReconnect(url); - } - } - /** - * Schedule reconnection attempt - */ - scheduleReconnect(url) { - const existing = this.reconnectTimeouts.get(url); - if (existing) - clearTimeout(existing); - const timeout = setTimeout(() => { - this.reconnectTimeouts.delete(url); - this.connect(url); - }, 5000); // 5 second delay - this.reconnectTimeouts.set(url, timeout); - } - /** - * Update relay status and notify callbacks - */ - updateStatus(url, updates) { - const current = this.status.get(url) || { url, connected: false }; - const updated = { ...current, ...updates }; - this.status.set(url, updated); - // Notify callbacks - this.statusCallbacks.forEach((cb) => cb(updated)); - } - /** - * Get WebSocket for a relay - */ - getRelay(url) { - return this.relays.get(url) || null; - } - /** - * Get all connected relays - */ - getConnectedRelays() { - return Array.from(this.relays.entries()) - .filter(([, ws]) => ws && ws.readyState === WebSocket.OPEN) - .map(([url]) => url); - } - /** - * Get relay status - */ - getStatus(url) { - return this.status.get(url); - } - /** - * Get all relay statuses - */ - getAllStatuses() { - return Array.from(this.status.values()); - } - /** - * Subscribe to status updates - */ - onStatusUpdate(callback) { - this.statusCallbacks.add(callback); - return () => this.statusCallbacks.delete(callback); - } - /** - * Send message to relay - */ - send(url, message) { - const ws = this.relays.get(url); - if (ws && ws.readyState === WebSocket.OPEN) { - ws.send(message); - return true; - } - return false; - } - /** - * Send message to all connected relays - */ - broadcast(message) { - const sent = []; - for (const [url, ws] of this.relays.entries()) { - if (ws && ws.readyState === WebSocket.OPEN) { - ws.send(message); - sent.push(url); - } - } - return sent; - } - /** - * Close all connections - */ - closeAll() { - for (const [url, ws] of this.relays.entries()) { - if (ws) { - ws.close(); - } - const timeout = this.reconnectTimeouts.get(url); - if (timeout) { - clearTimeout(timeout); - this.reconnectTimeouts.delete(url); - } - } - this.relays.clear(); - this.status.clear(); - } -} -export const relayPool = new RelayPool(); -// Initialize with default relays -export async function initializeRelayPool() { - for (const url of config.defaultRelays) { - await relayPool.addRelay(url); - } -} -//# sourceMappingURL=relay-pool.js.map \ No newline at end of file diff --git a/src/lib/services/nostr/relay-pool.js.map b/src/lib/services/nostr/relay-pool.js.map deleted file mode 100644 index 2e2b7e2..0000000 --- a/src/lib/services/nostr/relay-pool.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"relay-pool.js","sourceRoot":"","sources":["relay-pool.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/lib/services/nostr/subscription-manager.js b/src/lib/services/nostr/subscription-manager.js deleted file mode 100644 index 00cfed4..0000000 --- a/src/lib/services/nostr/subscription-manager.js +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Subscription manager for Nostr subscriptions - */ -import { relayPool } from './relay-pool.js'; -class SubscriptionManager { - subscriptions = new Map(); - nextSubId = 1; - /** - * Create a new subscription - */ - subscribe(subId, relays, filters, onEvent, onEose) { - // Close existing subscription if any - this.unsubscribe(subId); - const subscription = { - id: subId, - relays, - filters, - onEvent, - onEose, - messageHandlers: new Map() - }; - // Set up message handlers for each relay - for (const relayUrl of relays) { - const ws = relayPool.getRelay(relayUrl); - if (!ws) - continue; - const handler = (event) => { - try { - const data = JSON.parse(event.data); - if (Array.isArray(data)) { - const [type, ...rest] = data; - if (type === 'EVENT' && rest[0] === subId) { - const event = rest[1]; - if (this.matchesFilters(event, filters)) { - onEvent(event, relayUrl); - } - } - else if (type === 'EOSE' && rest[0] === subId) { - onEose?.(relayUrl); - } - } - } - catch (error) { - console.error('Error parsing relay message:', error); - } - }; - ws.addEventListener('message', handler); - subscription.messageHandlers.set(relayUrl, handler); - // Send subscription request - const message = JSON.stringify(['REQ', subId, ...filters]); - relayPool.send(relayUrl, message); - } - this.subscriptions.set(subId, subscription); - } - /** - * Check if event matches filters - */ - matchesFilters(event, filters) { - return filters.some((filter) => { - if (filter.ids && !filter.ids.includes(event.id)) - return false; - if (filter.authors && !filter.authors.includes(event.pubkey)) - return false; - if (filter.kinds && !filter.kinds.includes(event.kind)) - return false; - if (filter.since && event.created_at < filter.since) - return false; - if (filter.until && event.created_at > filter.until) - return false; - // Tag filters - if (filter['#e']) { - const hasE = event.tags.some((t) => t[0] === 'e' && filter['#e'].includes(t[1])); - if (!hasE) - return false; - } - if (filter['#p']) { - const hasP = event.tags.some((t) => t[0] === 'p' && filter['#p'].includes(t[1])); - if (!hasP) - return false; - } - return true; - }); - } - /** - * Unsubscribe from a subscription - */ - unsubscribe(subId) { - const subscription = this.subscriptions.get(subId); - if (!subscription) - return; - // Remove message handlers - for (const [relayUrl, handler] of subscription.messageHandlers.entries()) { - const ws = relayPool.getRelay(relayUrl); - if (ws) { - ws.removeEventListener('message', handler); - } - // Send close message - const message = JSON.stringify(['CLOSE', subId]); - relayPool.send(relayUrl, message); - } - this.subscriptions.delete(subId); - } - /** - * Generate a unique subscription ID - */ - generateSubId() { - return `sub_${this.nextSubId++}_${Date.now()}`; - } - /** - * Close all subscriptions - */ - closeAll() { - for (const subId of this.subscriptions.keys()) { - this.unsubscribe(subId); - } - } -} -export const subscriptionManager = new SubscriptionManager(); -//# sourceMappingURL=subscription-manager.js.map \ No newline at end of file diff --git a/src/lib/services/nostr/subscription-manager.js.map b/src/lib/services/nostr/subscription-manager.js.map deleted file mode 100644 index 6cf6abd..0000000 --- a/src/lib/services/nostr/subscription-manager.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"subscription-manager.js","sourceRoot":"","sources":["subscription-manager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/lib/services/security/bech32-utils.js b/src/lib/services/security/bech32-utils.js deleted file mode 100644 index c11a6bb..0000000 --- a/src/lib/services/security/bech32-utils.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Bech32 utilities for NIP-19 encoding/decoding - */ -/** - * Decode a bech32 string (simplified - full implementation would use bech32 library) - * This is a placeholder - in production, use a proper bech32 library - */ -export function decodeBech32(bech32) { - try { - const prefix = bech32.split('1')[0]; - if (!prefix) - return null; - // Basic validation - full implementation needed - if (prefix === 'npub' || prefix === 'nsec' || prefix === 'note') { - return { - type: prefix, - data: new Uint8Array(32) // Placeholder - }; - } - return null; - } - catch { - return null; - } -} -/** - * Encode data to bech32 format - */ -export function encodeBech32(type, data, relay) { - // Placeholder - full implementation needed with bech32 library - // For now, return hex representation - return `${type}1${Array.from(data) - .map((b) => b.toString(16).padStart(2, '0')) - .join('')}`; -} -/** - * Validate bech32 string format - */ -export function isValidBech32(bech32) { - return /^(npub|nsec|note|nevent|naddr|nprofile)1[a-z0-9]+$/.test(bech32); -} -//# sourceMappingURL=bech32-utils.js.map \ No newline at end of file diff --git a/src/lib/services/security/bech32-utils.js.map b/src/lib/services/security/bech32-utils.js.map deleted file mode 100644 index 677ac38..0000000 --- a/src/lib/services/security/bech32-utils.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"bech32-utils.js","sourceRoot":"","sources":["bech32-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAEzB,gDAAgD;QAChD,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YAChE,OAAO;gBACL,IAAI,EAAE,MAAkC;gBACxC,IAAI,EAAE,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,cAAc;aACxC,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,IAAgB,EAAE,KAAc;IACzE,+DAA+D;IAC/D,qCAAqC;IACrC,OAAO,GAAG,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;SAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SAC3C,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,oDAAoD,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC3E,CAAC"} \ No newline at end of file diff --git a/src/lib/services/security/event-validator.js b/src/lib/services/security/event-validator.js deleted file mode 100644 index 9808700..0000000 --- a/src/lib/services/security/event-validator.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Event validation utilities - */ -/** - * Validate event structure - */ -export function isValidEvent(event) { - if (!event || typeof event !== 'object') - return false; - const e = event; - return (typeof e.kind === 'number' && - typeof e.pubkey === 'string' && - typeof e.created_at === 'number' && - typeof e.content === 'string' && - typeof e.id === 'string' && - typeof e.sig === 'string' && - Array.isArray(e.tags) && - e.pubkey.length === 64 && - e.id.length === 64 && - e.sig.length === 128); -} -/** - * Check if event has required tags for a kind - */ -export function hasRequiredTags(event, kind) { - switch (kind) { - case 0: - // Kind 0 can have tags or JSON content - return true; - case 11: - // Thread - should have title tag - return true; - case 1111: - // Comment - should have K and E tags - return event.tags.some((t) => t[0] === 'K' || t[0] === 'E'); - default: - return true; - } -} -/** - * Validate event signature (placeholder - would need crypto library) - */ -export function isValidSignature(event) { - // Placeholder - full implementation would verify signature - // using secp256k1 cryptography - return event.sig.length === 128; -} -//# sourceMappingURL=event-validator.js.map \ No newline at end of file diff --git a/src/lib/services/security/event-validator.js.map b/src/lib/services/security/event-validator.js.map deleted file mode 100644 index 3d62e3c..0000000 --- a/src/lib/services/security/event-validator.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"event-validator.js","sourceRoot":"","sources":["event-validator.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAEtD,MAAM,CAAC,GAAG,KAAgC,CAAC;IAE3C,OAAO,CACL,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ;QAC1B,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ;QAC5B,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ;QAChC,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ;QAC7B,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ;QACxB,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QACrB,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,EAAE;QACtB,CAAC,CAAC,EAAE,CAAC,MAAM,KAAK,EAAE;QAClB,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG,CACrB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAiB,EAAE,IAAY;IAC7D,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,CAAC;YACJ,uCAAuC;YACvC,OAAO,IAAI,CAAC;QACd,KAAK,EAAE;YACL,iCAAiC;YACjC,OAAO,IAAI,CAAC;QACd,KAAK,IAAI;YACP,qCAAqC;YACrC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;QAC9D;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IAChD,2DAA2D;IAC3D,+BAA+B;IAC/B,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC;AAClC,CAAC"} \ No newline at end of file diff --git a/src/lib/services/security/key-management.js b/src/lib/services/security/key-management.js deleted file mode 100644 index 3eaa5cc..0000000 --- a/src/lib/services/security/key-management.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Key management with NIP-49 encryption - * All private keys MUST be encrypted before storage - */ -/** - * Encrypt a private key using NIP-49 (password-based encryption) - * This is a placeholder - full implementation requires: - * - scrypt for key derivation - * - AES-256-GCM for encryption - * - Base64 encoding - * - * WARNING: Current implementation stores keys in plaintext format. - * This is insecure and should only be used for development. - * Production code MUST implement proper NIP-49 encryption. - */ -export async function encryptPrivateKey(nsec, password) { - // TEMPORARY: Store as base64-encoded plaintext with a marker - // This allows the system to function while proper crypto is implemented - // Full NIP-49 implementation would: - // 1. Derive key from password using scrypt - // 2. Generate random salt and nonce - // 3. Encrypt nsec with AES-256-GCM - // 4. Encode as ncryptsec format - const encoded = btoa(JSON.stringify({ nsec, password })); - return `ncryptsec1${encoded}`; -} -/** - * Decrypt a private key using NIP-49 - * - * WARNING: Current implementation reads plaintext keys. - * This is insecure and should only be used for development. - * Production code MUST implement proper NIP-49 decryption. - */ -export async function decryptPrivateKey(ncryptsec, password) { - // TEMPORARY: Decode from base64 plaintext format - // This allows the system to function while proper crypto is implemented - // Full NIP-49 implementation would: - // 1. Decode ncryptsec format - // 2. Derive key from password using scrypt - // 3. Decrypt with AES-256-GCM - // 4. Return plain nsec - if (!ncryptsec.startsWith('ncryptsec1')) { - throw new Error('Invalid ncryptsec format'); - } - try { - const decoded = JSON.parse(atob(ncryptsec.slice(11))); - if (decoded.password !== password) { - throw new Error('Invalid password'); - } - return decoded.nsec; - } - catch (error) { - throw new Error('Failed to decrypt private key: ' + (error instanceof Error ? error.message : 'Unknown error')); - } -} -/** - * Generate a new private key - */ -export function generatePrivateKey() { - // Placeholder - would use crypto.getRandomValues to generate 32 random bytes - // then encode as hex - const array = new Uint8Array(32); - crypto.getRandomValues(array); - return Array.from(array) - .map((b) => b.toString(16).padStart(2, '0')) - .join(''); -} -//# sourceMappingURL=key-management.js.map \ No newline at end of file diff --git a/src/lib/services/security/key-management.js.map b/src/lib/services/security/key-management.js.map deleted file mode 100644 index 7931c3d..0000000 --- a/src/lib/services/security/key-management.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"key-management.js","sourceRoot":"","sources":["key-management.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY,EAAE,QAAgB;IACpE,6DAA6D;IAC7D,wEAAwE;IACxE,oCAAoC;IACpC,2CAA2C;IAC3C,oCAAoC;IACpC,mCAAmC;IACnC,gCAAgC;IAChC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;IACzD,OAAO,aAAa,OAAO,EAAE,CAAC;AAChC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,SAAiB,EAAE,QAAgB;IACzE,iDAAiD;IACjD,wEAAwE;IACxE,oCAAoC;IACpC,6BAA6B;IAC7B,2CAA2C;IAC3C,8BAA8B;IAC9B,uBAAuB;IACvB,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACtD,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,OAAO,CAAC,IAAI,CAAC;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,iCAAiC,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC;IAClH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,6EAA6E;IAC7E,qBAAqB;IACrB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IACjC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAC9B,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;SACrB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SAC3C,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC"} \ No newline at end of file diff --git a/src/lib/services/security/sanitizer.js b/src/lib/services/security/sanitizer.js deleted file mode 100644 index 39a3d57..0000000 --- a/src/lib/services/security/sanitizer.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * HTML sanitization using DOMPurify - */ -import DOMPurify from 'dompurify'; -/** - * Sanitize HTML content - */ -export function sanitizeHtml(dirty) { - return DOMPurify.sanitize(dirty, { - ALLOWED_TAGS: [ - 'p', - 'br', - 'strong', - 'em', - 'u', - 's', - 'code', - 'pre', - 'a', - 'ul', - 'ol', - 'li', - 'blockquote', - 'h1', - 'h2', - 'h3', - 'h4', - 'h5', - 'h6', - 'img', - 'video', - 'audio' - ], - ALLOWED_ATTR: ['href', 'src', 'alt', 'title', 'class', 'controls', 'preload'], - ALLOW_DATA_ATTR: false - }); -} -/** - * Sanitize markdown-rendered HTML - */ -export function sanitizeMarkdown(html) { - return sanitizeHtml(html); -} -//# sourceMappingURL=sanitizer.js.map \ No newline at end of file diff --git a/src/lib/services/security/sanitizer.js.map b/src/lib/services/security/sanitizer.js.map deleted file mode 100644 index dfee08c..0000000 --- a/src/lib/services/security/sanitizer.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"sanitizer.js","sourceRoot":"","sources":["sanitizer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,SAAS,MAAM,WAAW,CAAC;AAElC;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAO,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE;QAC/B,YAAY,EAAE;YACZ,GAAG;YACH,IAAI;YACJ,QAAQ;YACR,IAAI;YACJ,GAAG;YACH,GAAG;YACH,MAAM;YACN,KAAK;YACL,GAAG;YACH,IAAI;YACJ,IAAI;YACJ,IAAI;YACJ,YAAY;YACZ,IAAI;YACJ,IAAI;YACJ,IAAI;YACJ,IAAI;YACJ,IAAI;YACJ,IAAI;YACJ,KAAK;YACL,OAAO;YACP,OAAO;SACR;QACD,YAAY,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,CAAC;QAC7E,eAAe,EAAE,KAAK;KACvB,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC"} \ No newline at end of file diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 7a4e65b..787a0a6 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,5 +1,16 @@ diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 4c0d948..fbcbe22 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -5,7 +5,11 @@ import { onMount } from 'svelte'; onMount(async () => { - await nostrClient.initialize(); + try { + await nostrClient.initialize(); + } catch (error) { + console.error('Failed to initialize Nostr client:', error); + } });
Loading threads...
No threads found in this topic.