// deno-lint-ignore-file require-await import { NCache } from '@nostrify/nostrify'; import { Conf } from '@/config.ts'; import { db } from '@/db.ts'; import { EventsDB } from '@/storages/events-db.ts'; import { Optimizer } from '@/storages/optimizer.ts'; import { PoolStore } from '@/storages/pool-store.ts'; import { Reqmeister } from '@/storages/reqmeister.ts'; import { SearchStore } from '@/storages/search-store.ts'; import { InternalRelay } from '@/storages/InternalRelay.ts'; import { UserStore } from '@/storages/UserStore.ts'; import { Time } from '@/utils/time.ts'; export class Storages { private static _db: Promise | undefined; private static _admin: Promise | undefined; private static _cache: Promise | undefined; private static _client: Promise | undefined; private static _optimizer: Promise | undefined; private static _reqmeister: Promise | undefined; private static _pubsub: Promise | undefined; private static _search: Promise | undefined; /** SQLite database to store events this Ditto server cares about. */ public static async db(): Promise { if (!this._db) { this._db = Promise.resolve(new EventsDB(db)); } return this._db; } /** Admin user storage. */ public static async admin(): Promise { if (!this._admin) { this._admin = Promise.resolve(new UserStore(Conf.pubkey, await this.db())); } return this._admin; } /** Internal pubsub relay between controllers and the pipeline. */ public static async pubsub(): Promise { if (!this._pubsub) { this._pubsub = Promise.resolve(new InternalRelay()); } return this._pubsub; } /** Relay pool storage. */ public static async client(): Promise { if (!this._client) { this._client = (async () => { const db = await this.db(); const [relayList] = await db.query([ { kinds: [10002], authors: [Conf.pubkey], limit: 1 }, ]); const tags = relayList?.tags ?? []; const activeRelays = tags.reduce((acc, [name, url, marker]) => { if (name === 'r' && !marker) { acc.push(url); } return acc; }, []); console.log(`pool: connecting to ${activeRelays.length} relays.`); const worker = new Worker('https://unpkg.com/nostr-relaypool2@0.6.34/lib/nostr-relaypool.worker.js', { type: 'module', }); // @ts-ignore Wrong types. const pool = new RelayPoolWorker(worker, activeRelays, { autoReconnect: true, // The pipeline verifies events. skipVerification: true, // The logging feature overwhelms the CPU and creates too many logs. logErrorsAndNotices: false, }); return new PoolStore({ pool, relays: activeRelays, }); })(); } return this._client; } /** In-memory data store for cached events. */ public static async cache(): Promise { if (!this._cache) { this._cache = Promise.resolve(new NCache({ max: 3000 })); } return this._cache; } /** Batches requests for single events. */ public static async reqmeister(): Promise { if (!this._reqmeister) { this._reqmeister = Promise.resolve( new Reqmeister({ client: await this.client(), delay: Time.seconds(1), timeout: Time.seconds(1), }), ); } return this._reqmeister; } /** Main Ditto storage adapter */ public static async optimizer(): Promise { if (!this._optimizer) { this._optimizer = Promise.resolve( new Optimizer({ db: await this.db(), cache: await this.cache(), client: await this.reqmeister(), }), ); } return this._optimizer; } /** Storage to use for remote search. */ public static async search(): Promise { if (!this._search) { this._search = Promise.resolve( new SearchStore({ relay: Conf.searchRelay, fallback: await this.optimizer(), }), ); } return this._search; } }