Browse Source

bug-fixes

master
Silberengel 1 month ago
parent
commit
31328eaa26
  1. 4
      src/lib/components/content/GifPicker.svelte
  2. 6
      src/lib/services/cache/cache-manager.ts
  3. 68
      src/lib/services/cache/deletion-tracker.ts
  4. 49
      src/lib/services/cache/event-cache.ts
  5. 2
      src/lib/services/cache/indexeddb-store.ts
  6. 16
      src/routes/cache/+page.svelte

4
src/lib/components/content/GifPicker.svelte

@ -342,7 +342,7 @@
pendingUpload = { file, fileUrl }; pendingUpload = { file, fileUrl };
showMetadataForm = true; showMetadataForm = true;
// Reset metadata form // Reset metadata form and prefill image URL with uploaded file URL
metadataForm = { metadataForm = {
title: '', title: '',
summary: '', summary: '',
@ -350,7 +350,7 @@
dim: '', dim: '',
blurhash: '', blurhash: '',
thumb: '', thumb: '',
image: '', image: fileUrl, // Prefill with the uploaded GIF URL
content: '' content: ''
}; };
} catch (error) { } catch (error) {

6
src/lib/services/cache/cache-manager.ts vendored

@ -140,6 +140,12 @@ export async function getAllCachedEvents(
await tx.done; await tx.done;
// Filter out events that have deletion requests
const { getDeletedEventIds } = await import('./deletion-tracker.js');
const eventIds = events.map(e => e.id);
const deletedIds = await getDeletedEventIds(eventIds);
events = events.filter(e => !deletedIds.has(e.id));
// Apply search filter if provided // Apply search filter if provided
if (options.searchTerm) { if (options.searchTerm) {
const searchLower = options.searchTerm.toLowerCase(); const searchLower = options.searchTerm.toLowerCase();

68
src/lib/services/cache/deletion-tracker.ts vendored

@ -0,0 +1,68 @@
/**
* Track deletion requests to prevent re-caching deleted events
* Uses cached kind 5 (deletion request) events from the cache
*/
import { getDB } from './indexeddb-store.js';
import { KIND } from '../../types/kind-lookup.js';
import type { CachedEvent } from './event-cache.js';
/**
* Get all event IDs that have deletion requests
* Queries the cache for kind 5 events and extracts event IDs from their 'e' tags
*/
async function getDeletedEventIdsFromCache(): Promise<Set<string>> {
const deletedSet = new Set<string>();
try {
const db = await getDB();
const tx = db.transaction('events', 'readonly');
const index = tx.store.index('kind');
// Get all kind 5 (deletion request) events
for await (const cursor of index.iterate(KIND.EVENT_DELETION)) {
const deletionEvent = cursor.value as CachedEvent;
// Extract event IDs from 'e' tags
if (deletionEvent.tags) {
for (const tag of deletionEvent.tags) {
if (tag[0] === 'e' && tag[1]) {
deletedSet.add(tag[1]);
}
}
}
}
await tx.done;
} catch (error) {
console.debug('Error getting deleted event IDs from cache:', error);
}
return deletedSet;
}
/**
* Check if an event is marked as deleted (has a deletion request in cache)
*/
export async function isEventDeleted(eventId: string): Promise<boolean> {
try {
const deletedIds = await getDeletedEventIdsFromCache();
return deletedIds.has(eventId);
} catch (error) {
console.debug('Error checking if event is deleted:', error);
return false;
}
}
/**
* Check if any of the given event IDs are marked as deleted
* Returns a Set of deleted event IDs for efficient lookup
*/
export async function getDeletedEventIds(eventIds: string[]): Promise<Set<string>> {
try {
const deletedIds = await getDeletedEventIdsFromCache();
// Return intersection of requested IDs and deleted IDs
return new Set(eventIds.filter(id => deletedIds.has(id)));
} catch (error) {
console.debug('Error checking deleted events:', error);
return new Set<string>();
}
}

49
src/lib/services/cache/event-cache.ts vendored

@ -3,6 +3,7 @@
*/ */
import { getDB } from './indexeddb-store.js'; import { getDB } from './indexeddb-store.js';
import { isEventDeleted, getDeletedEventIds } from './deletion-tracker.js';
import type { NostrEvent } from '../../types/nostr.js'; import type { NostrEvent } from '../../types/nostr.js';
export interface CachedEvent extends NostrEvent { export interface CachedEvent extends NostrEvent {
@ -14,12 +15,17 @@ export interface CachedEvent extends NostrEvent {
*/ */
export async function cacheEvent(event: NostrEvent): Promise<void> { export async function cacheEvent(event: NostrEvent): Promise<void> {
try { try {
const db = await getDB(); // Don't cache events that have deletion requests
const cached: CachedEvent = { if (await isEventDeleted(event.id)) {
...event, return;
cached_at: Date.now() }
};
await db.put('events', cached); const db = await getDB();
const cached: CachedEvent = {
...event,
cached_at: Date.now()
};
await db.put('events', cached);
} catch (error) { } catch (error) {
console.debug('Error caching event:', error); console.debug('Error caching event:', error);
// Don't throw - caching failures shouldn't break the app // Don't throw - caching failures shouldn't break the app
@ -31,16 +37,27 @@ export async function cacheEvent(event: NostrEvent): Promise<void> {
*/ */
export async function cacheEvents(events: NostrEvent[]): Promise<void> { export async function cacheEvents(events: NostrEvent[]): Promise<void> {
try { try {
const db = await getDB(); if (events.length === 0) return;
const tx = db.transaction('events', 'readwrite');
for (const event of events) { // Check which events are marked as deleted
const cached: CachedEvent = { const eventIds = events.map(e => e.id);
...event, const deletedIds = await getDeletedEventIds(eventIds);
cached_at: Date.now()
}; // Filter out deleted events
await tx.store.put(cached); const eventsToCache = events.filter(e => !deletedIds.has(e.id));
}
await tx.done; if (eventsToCache.length === 0) return;
const db = await getDB();
const tx = db.transaction('events', 'readwrite');
for (const event of eventsToCache) {
const cached: CachedEvent = {
...event,
cached_at: Date.now()
};
await tx.store.put(cached);
}
await tx.done;
} catch (error) { } catch (error) {
console.debug('Error caching events:', error); console.debug('Error caching events:', error);
// Don't throw - caching failures shouldn't break the app // Don't throw - caching failures shouldn't break the app

2
src/lib/services/cache/indexeddb-store.ts vendored

@ -5,7 +5,7 @@
import { openDB, type IDBPDatabase } from 'idb'; import { openDB, type IDBPDatabase } from 'idb';
const DB_NAME = 'aitherboard'; const DB_NAME = 'aitherboard';
const DB_VERSION = 2; // Incremented to force upgrade when stores are missing const DB_VERSION = 3; // Incremented to add deletion_requests store
export interface DatabaseSchema { export interface DatabaseSchema {
events: { events: {

16
src/routes/cache/+page.svelte vendored

@ -3,11 +3,12 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { getCacheStats, getAllCachedEvents, clearAllCache, clearCacheByKind, clearCacheByKinds, clearCacheByDate, deleteEventById, type CacheStats } from '../../lib/services/cache/cache-manager.js'; import { getCacheStats, getAllCachedEvents, clearAllCache, clearCacheByKind, clearCacheByKinds, clearCacheByDate, deleteEventById, type CacheStats } from '../../lib/services/cache/cache-manager.js';
import { cacheEvent } from '../../lib/services/cache/event-cache.js';
import type { CachedEvent } from '../../lib/services/cache/event-cache.js'; import type { CachedEvent } from '../../lib/services/cache/event-cache.js';
import { KIND, getKindInfo } from '../../lib/types/kind-lookup.js'; import { KIND, getKindInfo } from '../../lib/types/kind-lookup.js';
import { nip19 } from 'nostr-tools'; import { nip19 } from 'nostr-tools';
import { sessionManager } from '../../lib/services/auth/session-manager.js'; import { sessionManager } from '../../lib/services/auth/session-manager.js';
import { signAndPublish } from '../../lib/services/nostr/auth-handler.js'; import { nostrClient } from '../../lib/services/nostr/nostr-client.js';
import type { NostrEvent } from '../../lib/types/nostr.js'; import type { NostrEvent } from '../../lib/types/nostr.js';
let stats = $state<CacheStats | null>(null); let stats = $state<CacheStats | null>(null);
@ -352,9 +353,18 @@
content: '' content: ''
}; };
const result = await signAndPublish(deleteEvent); // Sign the deletion request event first
const signedDeleteEvent = await sessionManager.signEvent(deleteEvent);
// Cache the deletion request event so it prevents re-caching of the deleted event
// This must happen before publishing to ensure it's in cache
await cacheEvent(signedDeleteEvent);
// Publish the already-signed event directly
// Note: nostrClient.publish() will also cache it, but that's fine (idempotent)
const result = await nostrClient.publish(signedDeleteEvent);
if (result.success.length > 0) { if (result.success.length > 0) {
// Also delete from cache // Also delete the original event from cache
try { try {
await deleteEventById(event.id); await deleteEventById(event.id);
events = events.filter(e => e.id !== event.id); events = events.filter(e => e.id !== event.id);

Loading…
Cancel
Save