Merge branch 'rm-relays' into 'main'
Remove `relays` table from the database, track them with a NIP-65 admin event See merge request soapbox-pub/ditto!207
This commit is contained in:
commit
79177cd6c3
|
@ -7,7 +7,6 @@
|
||||||
"debug": "deno run -A --inspect src/server.ts",
|
"debug": "deno run -A --inspect src/server.ts",
|
||||||
"test": "DATABASE_URL=\"sqlite://:memory:\" deno test -A",
|
"test": "DATABASE_URL=\"sqlite://:memory:\" deno test -A",
|
||||||
"check": "deno check src/server.ts",
|
"check": "deno check src/server.ts",
|
||||||
"relays:sync": "deno run -A scripts/relays.ts sync",
|
|
||||||
"nsec": "deno run scripts/nsec.ts",
|
"nsec": "deno run scripts/nsec.ts",
|
||||||
"admin:event": "deno run -A scripts/admin-event.ts",
|
"admin:event": "deno run -A scripts/admin-event.ts",
|
||||||
"admin:role": "deno run -A scripts/admin-role.ts"
|
"admin:role": "deno run -A scripts/admin-role.ts"
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
import { addRelays } from '@/db/relays.ts';
|
|
||||||
import { filteredArray } from '@/schema.ts';
|
|
||||||
import { relaySchema } from '@/utils.ts';
|
|
||||||
|
|
||||||
switch (Deno.args[0]) {
|
|
||||||
case 'sync':
|
|
||||||
await sync(Deno.args.slice(1));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.log('Usage: deno run -A scripts/relays.ts sync <url>');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sync([url]: string[]) {
|
|
||||||
if (!url) {
|
|
||||||
console.error('Error: please provide a URL');
|
|
||||||
Deno.exit(1);
|
|
||||||
}
|
|
||||||
const response = await fetch(url);
|
|
||||||
const data = await response.json();
|
|
||||||
const values = filteredArray(relaySchema).parse(data) as `wss://${string}`[];
|
|
||||||
await addRelays(values, { active: true });
|
|
||||||
console.log(`Done: added ${values.length} relays.`);
|
|
||||||
}
|
|
|
@ -2,7 +2,6 @@ export interface DittoTables {
|
||||||
events: EventRow;
|
events: EventRow;
|
||||||
events_fts: EventFTSRow;
|
events_fts: EventFTSRow;
|
||||||
tags: TagRow;
|
tags: TagRow;
|
||||||
relays: RelayRow;
|
|
||||||
unattached_media: UnattachedMediaRow;
|
unattached_media: UnattachedMediaRow;
|
||||||
author_stats: AuthorStatsRow;
|
author_stats: AuthorStatsRow;
|
||||||
event_stats: EventStatsRow;
|
event_stats: EventStatsRow;
|
||||||
|
@ -45,12 +44,6 @@ interface TagRow {
|
||||||
event_id: string;
|
event_id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RelayRow {
|
|
||||||
url: string;
|
|
||||||
domain: string;
|
|
||||||
active: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UnattachedMediaRow {
|
interface UnattachedMediaRow {
|
||||||
id: string;
|
id: string;
|
||||||
pubkey: string;
|
pubkey: string;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Kysely } from '@/deps.ts';
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
export async function up(db: Kysely<any>): Promise<void> {
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
await db.schema
|
await db.schema
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Kysely } from '@/deps.ts';
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
export async function up(db: Kysely<any>): Promise<void> {
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
await db.schema
|
await db.schema
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Kysely } from '@/deps.ts';
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
export async function up(_db: Kysely<any>): Promise<void> {
|
export async function up(_db: Kysely<any>): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Kysely } from '@/deps.ts';
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
export async function up(_db: Kysely<any>): Promise<void> {
|
export async function up(_db: Kysely<any>): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Kysely } from '@/deps.ts';
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
export async function up(_db: Kysely<any>): Promise<void> {
|
export async function up(_db: Kysely<any>): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Kysely } from '@/deps.ts';
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
export async function up(db: Kysely<any>): Promise<void> {
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
await db.schema
|
await db.schema
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Kysely } from '@/deps.ts';
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
export async function up(_db: Kysely<any>): Promise<void> {
|
export async function up(_db: Kysely<any>): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Kysely } from '@/deps.ts';
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
export async function up(db: Kysely<any>): Promise<void> {
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
await db.schema
|
await db.schema
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Kysely } from '@/deps.ts';
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
export async function up(db: Kysely<any>): Promise<void> {
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
await db.schema.dropTable('users').ifExists().execute();
|
await db.schema.dropTable('users').ifExists().execute();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Kysely } from '@/deps.ts';
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
export async function up(db: Kysely<any>): Promise<void> {
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
await db.schema
|
await db.schema
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Kysely } from '@/deps.ts';
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
export async function up(db: Kysely<any>): Promise<void> {
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
await db.schema.dropIndex('idx_tags_tag').execute();
|
await db.schema.dropIndex('idx_tags_tag').execute();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Kysely } from '@/deps.ts';
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
export async function up(db: Kysely<any>): Promise<void> {
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
await db.schema.alterTable('events').addColumn('deleted_at', 'integer').execute();
|
await db.schema.alterTable('events').addColumn('deleted_at', 'integer').execute();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Kysely } from '@/deps.ts';
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
export async function up(db: Kysely<any>): Promise<void> {
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
await db.schema.createIndex('idx_author_stats_pubkey').on('author_stats').column('pubkey').execute();
|
await db.schema.createIndex('idx_author_stats_pubkey').on('author_stats').column('pubkey').execute();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Kysely } from '@/deps.ts';
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
export async function up(db: Kysely<any>): Promise<void> {
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
await db.schema
|
await db.schema
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Kysely } from '@/deps.ts';
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
export async function up(db: Kysely<any>): Promise<void> {
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
await db.schema
|
await db.schema
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
|
await db.schema.dropTable('relays').execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(db: Kysely<any>): Promise<void> {
|
||||||
|
await db.schema
|
||||||
|
.createTable('relays')
|
||||||
|
.addColumn('url', 'text', (col) => col.primaryKey())
|
||||||
|
.addColumn('domain', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('active', 'boolean', (col) => col.notNull())
|
||||||
|
.execute();
|
||||||
|
}
|
|
@ -1,37 +0,0 @@
|
||||||
import tldts from 'tldts';
|
|
||||||
|
|
||||||
import { db } from '@/db.ts';
|
|
||||||
|
|
||||||
interface AddRelaysOpts {
|
|
||||||
active?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Inserts relays into the database, skipping duplicates. */
|
|
||||||
function addRelays(relays: `wss://${string}`[], opts: AddRelaysOpts = {}) {
|
|
||||||
if (!relays.length) return Promise.resolve();
|
|
||||||
const { active = false } = opts;
|
|
||||||
|
|
||||||
const values = relays.map((url) => ({
|
|
||||||
url: new URL(url).toString(),
|
|
||||||
domain: tldts.getDomain(url)!,
|
|
||||||
active,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return db.insertInto('relays')
|
|
||||||
.values(values)
|
|
||||||
.onConflict((oc) => oc.column('url').doNothing())
|
|
||||||
.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get a list of all known active relay URLs. */
|
|
||||||
async function getActiveRelays(): Promise<string[]> {
|
|
||||||
const rows = await db
|
|
||||||
.selectFrom('relays')
|
|
||||||
.select('relays.url')
|
|
||||||
.where('relays.active', '=', true)
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
return rows.map((row) => row.url);
|
|
||||||
}
|
|
||||||
|
|
||||||
export { addRelays, getActiveRelays };
|
|
|
@ -5,7 +5,6 @@ import { sql } from 'kysely';
|
||||||
|
|
||||||
import { Conf } from '@/config.ts';
|
import { Conf } from '@/config.ts';
|
||||||
import { db } from '@/db.ts';
|
import { db } from '@/db.ts';
|
||||||
import { addRelays } from '@/db/relays.ts';
|
|
||||||
import { deleteAttachedMedia } from '@/db/unattached-media.ts';
|
import { deleteAttachedMedia } from '@/db/unattached-media.ts';
|
||||||
import { DittoEvent } from '@/interfaces/DittoEvent.ts';
|
import { DittoEvent } from '@/interfaces/DittoEvent.ts';
|
||||||
import { isEphemeralKind } from '@/kinds.ts';
|
import { isEphemeralKind } from '@/kinds.ts';
|
||||||
|
@ -14,7 +13,7 @@ import { updateStats } from '@/stats.ts';
|
||||||
import { hydrateEvents, purifyEvent } from '@/storages/hydrate.ts';
|
import { hydrateEvents, purifyEvent } from '@/storages/hydrate.ts';
|
||||||
import { Storages } from '@/storages.ts';
|
import { Storages } from '@/storages.ts';
|
||||||
import { getTagSet } from '@/tags.ts';
|
import { getTagSet } from '@/tags.ts';
|
||||||
import { eventAge, isRelay, nostrDate, nostrNow, parseNip05, Time } from '@/utils.ts';
|
import { eventAge, nostrDate, nostrNow, parseNip05, Time } from '@/utils.ts';
|
||||||
import { fetchWorker } from '@/workers/fetch.ts';
|
import { fetchWorker } from '@/workers/fetch.ts';
|
||||||
import { TrendsWorker } from '@/workers/trends.ts';
|
import { TrendsWorker } from '@/workers/trends.ts';
|
||||||
import { verifyEventWorker } from '@/workers/verify.ts';
|
import { verifyEventWorker } from '@/workers/verify.ts';
|
||||||
|
@ -59,7 +58,6 @@ async function handleEvent(event: DittoEvent, signal: AbortSignal): Promise<void
|
||||||
parseMetadata(event, signal),
|
parseMetadata(event, signal),
|
||||||
processDeletions(event, signal),
|
processDeletions(event, signal),
|
||||||
DVM.event(event),
|
DVM.event(event),
|
||||||
trackRelays(event),
|
|
||||||
trackHashtags(event),
|
trackHashtags(event),
|
||||||
fetchRelatedEvents(event, signal),
|
fetchRelatedEvents(event, signal),
|
||||||
processMedia(event),
|
processMedia(event),
|
||||||
|
@ -183,22 +181,6 @@ async function trackHashtags(event: NostrEvent): Promise<void> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Tracks known relays in the database. */
|
|
||||||
function trackRelays(event: NostrEvent) {
|
|
||||||
const relays = new Set<`wss://${string}`>();
|
|
||||||
|
|
||||||
event.tags.forEach((tag) => {
|
|
||||||
if (['p', 'e', 'a'].includes(tag[0]) && isRelay(tag[2])) {
|
|
||||||
relays.add(tag[2]);
|
|
||||||
}
|
|
||||||
if (event.kind === 10002 && tag[0] === 'r' && isRelay(tag[1])) {
|
|
||||||
relays.add(tag[1]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return addRelays([...relays]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Queue related events to fetch. */
|
/** Queue related events to fetch. */
|
||||||
async function fetchRelatedEvents(event: DittoEvent, signal: AbortSignal) {
|
async function fetchRelatedEvents(event: DittoEvent, signal: AbortSignal) {
|
||||||
if (!event.user) {
|
if (!event.user) {
|
||||||
|
|
16
src/pool.ts
16
src/pool.ts
|
@ -1,8 +1,20 @@
|
||||||
import { RelayPoolWorker } from 'nostr-relaypool';
|
import { RelayPoolWorker } from 'nostr-relaypool';
|
||||||
|
|
||||||
import { getActiveRelays } from '@/db/relays.ts';
|
import { Storages } from '@/storages.ts';
|
||||||
|
import { Conf } from '@/config.ts';
|
||||||
|
|
||||||
const activeRelays = await getActiveRelays();
|
const [relayList] = await Storages.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.`);
|
console.log(`pool: connecting to ${activeRelays.length} relays.`);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { NostrEvent } from '@nostrify/nostrify';
|
import { NostrEvent } from '@nostrify/nostrify';
|
||||||
import { EventTemplate, getEventHash, nip19 } from 'nostr-tools';
|
import { EventTemplate, getEventHash, nip19 } from 'nostr-tools';
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
import { nostrIdSchema } from '@/schemas/nostr.ts';
|
import { nostrIdSchema } from '@/schemas/nostr.ts';
|
||||||
|
|
||||||
|
@ -82,12 +81,6 @@ async function sha256(message: string): Promise<string> {
|
||||||
return hashHex;
|
return hashHex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Schema to parse a relay URL. */
|
|
||||||
const relaySchema = z.string().max(255).startsWith('wss://').url();
|
|
||||||
|
|
||||||
/** Check whether the value is a valid relay URL. */
|
|
||||||
const isRelay = (relay: string): relay is `wss://${string}` => relaySchema.safeParse(relay).success;
|
|
||||||
|
|
||||||
/** Deduplicate events by ID. */
|
/** Deduplicate events by ID. */
|
||||||
function dedupeEvents(events: NostrEvent[]): NostrEvent[] {
|
function dedupeEvents(events: NostrEvent[]): NostrEvent[] {
|
||||||
return [...new Map(events.map((event) => [event.id, event])).values()];
|
return [...new Map(events.map((event) => [event.id, event])).values()];
|
||||||
|
@ -143,13 +136,11 @@ export {
|
||||||
eventMatchesTemplate,
|
eventMatchesTemplate,
|
||||||
findTag,
|
findTag,
|
||||||
isNostrId,
|
isNostrId,
|
||||||
isRelay,
|
|
||||||
isURL,
|
isURL,
|
||||||
type Nip05,
|
type Nip05,
|
||||||
nostrDate,
|
nostrDate,
|
||||||
nostrNow,
|
nostrNow,
|
||||||
parseNip05,
|
parseNip05,
|
||||||
relaySchema,
|
|
||||||
sha256,
|
sha256,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue