Browse Source

Remove Applesauce. Only use nostr-tools.

master
Silberengel 1 month ago
parent
commit
845dc60764
  1. 9
      README.md
  2. 1
      README_SETUP.md
  3. 989
      package-lock.json
  4. 1
      package.json
  5. 4
      public/healthz.json
  6. 2
      src/lib/modules/threads/CreateThreadForm.svelte
  7. 2
      src/lib/modules/threads/ThreadList.svelte
  8. 6
      src/lib/services/auth/activity-tracker.ts
  9. 2
      src/lib/services/auth/profile-fetcher.ts
  10. 2
      src/lib/services/auth/relay-list-fetcher.ts
  11. 2
      src/lib/services/auth/user-status-fetcher.ts
  12. 2
      src/lib/services/nostr/auth-handler.ts
  13. 123
      src/lib/services/nostr/nostr-client.ts
  14. 2
      src/routes/+page.svelte
  15. 2
      src/routes/feed/+page.svelte
  16. 2
      src/routes/login/+page.svelte
  17. 2
      src/routes/thread/[id]/+page.svelte
  18. 2
      src/routes/threads/+page.svelte

9
README.md

@ -24,7 +24,7 @@ This is a client from [silberengel@gitcitadel.com](https://jumble.imwald.eu/user @@ -24,7 +24,7 @@ This is a client from [silberengel@gitcitadel.com](https://jumble.imwald.eu/user
**REQUIRED**:
- Frontend: Svelte 5 (with runes: `$state`, `$derived`, `$effect`) + TypeScript + Vite
- Styling: Tailwind CSS (4chan-style minimal design)
- Nostr Library: [`applesauce-core`](https://github.com/hzrd149/applesauce)
- Nostr Library: [`nostr-tools`](https://github.com/nbd-wtf/nostr-tools)
- Markdown: `marked` + `DOMPurify` for sanitization
- Storage: IndexedDB (via `idb` or `localforage`)
- Deployment: Docker + Apache httpd (static serving on port 9876)
@ -38,10 +38,7 @@ aitherboard/ @@ -38,10 +38,7 @@ aitherboard/
│ ├── lib/
│ │ ├── services/
│ │ │ ├── nostr/
│ │ │ │ ├── applesauce-client.ts
│ │ │ │ ├── relay-pool.ts
│ │ │ │ ├── event-store.ts
│ │ │ │ ├── subscription-manager.ts
│ │ │ │ ├── nostr-client.ts
│ │ │ │ ├── auth-handler.ts
│ │ │ │ └── config.ts
│ │ │ ├── auth/
@ -958,7 +955,7 @@ exec httpd -D FOREGROUND @@ -958,7 +955,7 @@ exec httpd -D FOREGROUND
## Links
- **Nostr Protocol**: https://nostr.com
- **Applesauce Library**: https://github.com/hzrd149/applesauce
- **nostr-tools Library**: https://github.com/nbd-wtf/nostr-tools
- **NIP-7D (Kind 11 Threads)**: [Nostr Improvement Proposals]
- **NIP-22 (Kind 1111 Comments)**: [Nostr Improvement Proposals]
- **NIP-57 (Zaps)**: [Nostr Improvement Proposals]

1
README_SETUP.md

@ -55,5 +55,4 @@ docker run -p 9876:9876 aitherboard @@ -55,5 +55,4 @@ docker run -p 9876:9876 aitherboard
- This is a work in progress. Many features are placeholders and need full implementation.
- NIP-49 encryption, event signing, and bech32 encoding need proper cryptographic libraries.
- The applesauce-core library integration needs to be completed.
- Full implementation of all modules (comments, zaps, reactions, profiles, feed) is ongoing.

989
package-lock.json generated

File diff suppressed because it is too large Load Diff

1
package.json

@ -25,7 +25,6 @@ @@ -25,7 +25,6 @@
"dependencies": {
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"applesauce-core": "github:hzrd149/applesauce",
"dompurify": "^3.0.6",
"idb": "^8.0.0",
"marked": "^11.1.1",

4
public/healthz.json

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
"status": "ok",
"service": "aitherboard",
"version": "0.1.0",
"buildTime": "2026-02-02T13:56:52.264Z",
"buildTime": "2026-02-02T14:12:17.639Z",
"gitCommit": "unknown",
"timestamp": 1770040612264
"timestamp": 1770041537639
}

2
src/lib/modules/threads/CreateThreadForm.svelte

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
<script lang="ts">
import { sessionManager } from '../../services/auth/session-manager.js';
import { nostrClient } from '../../services/nostr/applesauce-client.js';
import { nostrClient } from '../../services/nostr/nostr-client.js';
import type { NostrEvent } from '../../types/nostr.js';
let title = $state('');

2
src/lib/modules/threads/ThreadList.svelte

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
<script lang="ts">
import { nostrClient } from '../../services/nostr/applesauce-client.js';
import { nostrClient } from '../../services/nostr/nostr-client.js';
import { onMount } from 'svelte';
import ThreadCard from './ThreadCard.svelte';
import type { NostrEvent } from '../../types/nostr.js';

6
src/lib/services/auth/activity-tracker.ts

@ -2,21 +2,19 @@ @@ -2,21 +2,19 @@
* Activity tracker - tracks last activity per pubkey
*/
import { nostrClient } from '../nostr/applesauce-client.js';
import { nostrClient } from '../nostr/nostr-client.js';
import type { NostrEvent } from '../../types/nostr.js';
/**
* Get last activity timestamp for a pubkey
*/
export async function getLastActivity(pubkey: string): Promise<number | undefined> {
const eventStore = nostrClient.getEventStore();
// Query for recent events from this pubkey
const filters = [
{ authors: [pubkey], kinds: [0, 1, 7, 11, 1111], limit: 1 }
];
const events = eventStore.getByFilters(filters);
const events = nostrClient.getByFilters(filters);
if (events.length > 0) {
// Sort by created_at descending and return the most recent
const sorted = events.sort((a: NostrEvent, b: NostrEvent) => b.created_at - a.created_at);

2
src/lib/services/auth/profile-fetcher.ts

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
* Profile fetcher (kind 0 events)
*/
import { nostrClient } from '../nostr/applesauce-client.js';
import { nostrClient } from '../nostr/nostr-client.js';
import { cacheProfile, getProfile, getProfiles } from '../cache/profile-cache.js';
import { config } from '../nostr/config.js';
import type { NostrEvent } from '../../types/nostr.js';

2
src/lib/services/auth/relay-list-fetcher.ts

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
* Relay list fetcher (kind 10002 and 10432)
*/
import { nostrClient } from '../nostr/applesauce-client.js';
import { nostrClient } from '../nostr/nostr-client.js';
import { config } from '../nostr/config.js';
import type { NostrEvent } from '../../types/nostr.js';

2
src/lib/services/auth/user-status-fetcher.ts

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
* User status fetcher (kind 30315, NIP-38)
*/
import { nostrClient } from '../nostr/applesauce-client.js';
import { nostrClient } from '../nostr/nostr-client.js';
import { config } from '../nostr/config.js';
import type { NostrEvent } from '../../types/nostr.js';

2
src/lib/services/nostr/auth-handler.ts

@ -12,7 +12,7 @@ import { @@ -12,7 +12,7 @@ import {
import { decryptPrivateKey } from '../security/key-management.js';
import { sessionManager, type AuthMethod } from '../auth/session-manager.js';
import { fetchRelayLists } from '../auth/relay-list-fetcher.js';
import { nostrClient } from './applesauce-client.js';
import { nostrClient } from './nostr-client.js';
import type { NostrEvent } from '../../types/nostr.js';
// Mute list and blocked relays management

123
src/lib/services/nostr/applesauce-client.ts → src/lib/services/nostr/nostr-client.ts

@ -1,31 +1,24 @@ @@ -1,31 +1,24 @@
/**
* Applesauce-core client wrapper
* Main interface for Nostr operations using applesauce-core and nostr-tools
* Nostr client using nostr-tools
* Main interface for Nostr operations using only nostr-tools
*/
// @ts-expect-error - applesauce-core types may not be available, but package works at runtime
import { EventStore } from 'applesauce-core';
// @ts-expect-error - applesauce-core types may not be available, but package works at runtime
import type { Filter } from 'applesauce-core/helpers';
import { Relay } from 'nostr-tools/relay';
import { Relay, type Filter, matchFilter } from 'nostr-tools';
import { config } from './config.js';
import type { NostrEvent } from '../../types/nostr.js';
import { cacheEvent, cacheEvents, getEvent, getEventsByKind, getEventsByPubkey } from '../cache/event-cache.js';
export interface PublishOptions {
relays?: string[];
skipRelayValidation?: boolean;
}
class ApplesauceClient {
class NostrClient {
private initialized = false;
private eventStore: EventStore;
private relays: Map<string, Relay> = new Map();
private subscriptions: Map<string, { relay: Relay; sub: any }> = new Map();
private nextSubId = 1;
constructor() {
this.eventStore = new EventStore();
}
private eventCache: Map<string, NostrEvent> = new Map(); // In-memory cache
/**
* Initialize the client
@ -54,8 +47,6 @@ class ApplesauceClient { @@ -54,8 +47,6 @@ class ApplesauceClient {
try {
const relay = await Relay.connect(url);
this.relays.set(url, relay);
// Events will be added to store via subscriptions
} catch (error) {
console.error(`Failed to connect to relay ${url}:`, error);
throw error;
@ -73,6 +64,37 @@ class ApplesauceClient { @@ -73,6 +64,37 @@ class ApplesauceClient {
}
}
/**
* Add event to cache
*/
private addToCache(event: NostrEvent): void {
this.eventCache.set(event.id, event);
// Also cache to IndexedDB
cacheEvent(event).catch((error) => {
console.error('Error caching event:', error);
});
}
/**
* Get events from cache that match filters
*/
private getCachedEvents(filters: Filter[]): NostrEvent[] {
const results: NostrEvent[] = [];
const seen = new Set<string>();
for (const filter of filters) {
for (const event of this.eventCache.values()) {
if (seen.has(event.id)) continue;
if (matchFilter(filter, event)) {
results.push(event);
seen.add(event.id);
}
}
}
return results;
}
/**
* Publish an event to relays
*/
@ -86,8 +108,8 @@ class ApplesauceClient { @@ -86,8 +108,8 @@ class ApplesauceClient {
failed: [] as Array<{ relay: string; error: string }>
};
// Add event to store first
this.eventStore.add(event);
// Add event to cache first
this.addToCache(event);
// Publish to each relay
for (const url of relays) {
@ -176,8 +198,8 @@ class ApplesauceClient { @@ -176,8 +198,8 @@ class ApplesauceClient {
const client = this;
const sub = relay.subscribe(filters, {
onevent(event: NostrEvent) {
// Add to store
client.eventStore.add(event);
// Add to cache
client.addToCache(event);
// Call callback
onEvent(event, url);
},
@ -211,9 +233,35 @@ class ApplesauceClient { @@ -211,9 +233,35 @@ class ApplesauceClient {
): Promise<NostrEvent[]> {
const { useCache = true, cacheResults = true, onUpdate } = options || {};
// Query from event store first if cache is enabled
// Query from cache first if enabled
if (useCache) {
const cachedEvents = this.eventStore.getByFilters(filters);
// Try in-memory cache first
let cachedEvents = this.getCachedEvents(filters);
// If no results in memory, try IndexedDB
if (cachedEvents.length === 0) {
try {
// Try to get from IndexedDB based on filter
for (const filter of filters) {
if (filter.kinds && filter.kinds.length === 1) {
const dbEvents = await getEventsByKind(filter.kinds[0], filter.limit || 50);
cachedEvents.push(...dbEvents);
}
if (filter.authors && filter.authors.length === 1) {
const dbEvents = await getEventsByPubkey(filter.authors[0], filter.limit || 50);
cachedEvents.push(...dbEvents);
}
}
// Add to in-memory cache
for (const event of cachedEvents) {
this.eventCache.set(event.id, event);
}
} catch (error) {
console.error('Error loading from IndexedDB:', error);
}
}
if (cachedEvents.length > 0) {
// Return cached events immediately
if (onUpdate) {
@ -266,6 +314,14 @@ class ApplesauceClient { @@ -266,6 +314,14 @@ class ApplesauceClient {
}
const eventArrayValues = Array.from(eventArray);
// Cache results
if (options.cacheResults && eventArrayValues.length > 0) {
cacheEvents(eventArrayValues).catch((error) => {
console.error('Error caching events:', error);
});
}
if (options.onUpdate) {
options.onUpdate(eventArrayValues);
}
@ -307,9 +363,20 @@ class ApplesauceClient { @@ -307,9 +363,20 @@ class ApplesauceClient {
* Get event by ID
*/
async getEventById(id: string, relays: string[]): Promise<NostrEvent | null> {
// Try store first
const event = this.eventStore.getEvent(id);
if (event) return event;
// Try in-memory cache first
const cached = this.eventCache.get(id);
if (cached) return cached;
// Try IndexedDB
try {
const dbEvent = await getEvent(id);
if (dbEvent) {
this.eventCache.set(dbEvent.id, dbEvent);
return dbEvent;
}
} catch (error) {
console.error('Error loading from IndexedDB:', error);
}
// Fetch from relays
const filters: Filter[] = [{ ids: [id] }];
@ -318,10 +385,10 @@ class ApplesauceClient { @@ -318,10 +385,10 @@ class ApplesauceClient {
}
/**
* Get event store
* Get events by filters (from cache only)
*/
getEventStore(): EventStore {
return this.eventStore;
getByFilters(filters: Filter[]): NostrEvent[] {
return this.getCachedEvents(filters);
}
/**
@ -358,4 +425,4 @@ class ApplesauceClient { @@ -358,4 +425,4 @@ class ApplesauceClient {
}
}
export const nostrClient = new ApplesauceClient();
export const nostrClient = new NostrClient();

2
src/routes/+page.svelte

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
<script lang="ts">
import Header from '../lib/components/layout/Header.svelte';
import ThreadList from '../lib/modules/threads/ThreadList.svelte';
import { nostrClient } from '../lib/services/nostr/applesauce-client.js';
import { nostrClient } from '../lib/services/nostr/nostr-client.js';
import { onMount } from 'svelte';
onMount(async () => {

2
src/routes/feed/+page.svelte

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
<script lang="ts">
import Header from '../../lib/components/layout/Header.svelte';
import { nostrClient } from '../../lib/services/nostr/applesauce-client.js';
import { nostrClient } from '../../lib/services/nostr/nostr-client.js';
import { onMount } from 'svelte';
onMount(async () => {

2
src/routes/login/+page.svelte

@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
import { isNIP07Available } from '../../lib/services/auth/nip07-signer.js';
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
import { nostrClient } from '../../lib/services/nostr/applesauce-client.js';
import { nostrClient } from '../../lib/services/nostr/nostr-client.js';
onMount(async () => {
await nostrClient.initialize();

2
src/routes/thread/[id]/+page.svelte

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
import Header from '../../../lib/components/layout/Header.svelte';
import ProfileBadge from '../../../lib/components/layout/ProfileBadge.svelte';
import MarkdownRenderer from '../../../lib/components/content/MarkdownRenderer.svelte';
import { nostrClient } from '../../../lib/services/nostr/applesauce-client.js';
import { nostrClient } from '../../../lib/services/nostr/nostr-client.js';
import { onMount } from 'svelte';
import type { NostrEvent } from '../../../lib/types/nostr.js';
import { page } from '$app/stores';

2
src/routes/threads/+page.svelte

@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
import ThreadList from '../../lib/modules/threads/ThreadList.svelte';
import CreateThreadForm from '../../lib/modules/threads/CreateThreadForm.svelte';
import { sessionManager } from '../../lib/services/auth/session-manager.js';
import { nostrClient } from '../../lib/services/nostr/applesauce-client.js';
import { nostrClient } from '../../lib/services/nostr/nostr-client.js';
import { onMount } from 'svelte';
let showCreateForm = $state(false);

Loading…
Cancel
Save