mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
Merge branch 'main' into zap-notification-streaming
This commit is contained in:
commit
373e9ca6d8
10 changed files with 35 additions and 14 deletions
|
|
@ -77,6 +77,10 @@ class Conf {
|
||||||
static get testDatabaseUrl(): string {
|
static get testDatabaseUrl(): string {
|
||||||
return Deno.env.get('TEST_DATABASE_URL') ?? 'memory://';
|
return Deno.env.get('TEST_DATABASE_URL') ?? 'memory://';
|
||||||
}
|
}
|
||||||
|
/** PGlite debug level. 0 disables logging. */
|
||||||
|
static get pgliteDebug(): 0 | 1 | 2 | 3 | 4 | 5 {
|
||||||
|
return Number(Deno.env.get('PGLITE_DEBUG') || 0) as 0 | 1 | 2 | 3 | 4 | 5;
|
||||||
|
}
|
||||||
static db = {
|
static db = {
|
||||||
/** Database query timeout configurations. */
|
/** Database query timeout configurations. */
|
||||||
timeouts: {
|
timeouts: {
|
||||||
|
|
|
||||||
|
|
@ -381,7 +381,7 @@ const followersController: AppController = (c) => {
|
||||||
const followingController: AppController = async (c) => {
|
const followingController: AppController = async (c) => {
|
||||||
const pubkey = c.req.param('pubkey');
|
const pubkey = c.req.param('pubkey');
|
||||||
const pubkeys = await getFollowedPubkeys(pubkey);
|
const pubkeys = await getFollowedPubkeys(pubkey);
|
||||||
return renderAccounts(c, pubkeys);
|
return renderAccounts(c, [...pubkeys]);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** https://docs.joinmastodon.org/methods/accounts/#block */
|
/** https://docs.joinmastodon.org/methods/accounts/#block */
|
||||||
|
|
@ -460,7 +460,7 @@ const familiarFollowersController: AppController = async (c) => {
|
||||||
const follows = await getFollowedPubkeys(pubkey);
|
const follows = await getFollowedPubkeys(pubkey);
|
||||||
|
|
||||||
const results = await Promise.all(ids.map(async (id) => {
|
const results = await Promise.all(ids.map(async (id) => {
|
||||||
const followLists = await store.query([{ kinds: [3], authors: follows, '#p': [id] }])
|
const followLists = await store.query([{ kinds: [3], authors: [...follows], '#p': [id] }])
|
||||||
.then((events) => hydrateEvents({ events, store }));
|
.then((events) => hydrateEvents({ events, store }));
|
||||||
|
|
||||||
const accounts = await Promise.all(
|
const accounts = await Promise.all(
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ async function topicToFilter(
|
||||||
// HACK: this puts the user's entire contacts list into RAM,
|
// HACK: this puts the user's entire contacts list into RAM,
|
||||||
// and then calls `matchFilters` over it. Refreshing the page
|
// and then calls `matchFilters` over it. Refreshing the page
|
||||||
// is required after following a new user.
|
// is required after following a new user.
|
||||||
return pubkey ? { kinds: [1, 6, 9735], authors: await getFeedPubkeys(pubkey) } : undefined;
|
return pubkey ? { kinds: [1, 6], authors: [...await getFeedPubkeys(pubkey)] } : undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import { renderReblog, renderStatus } from '@/views/mastodon/statuses.ts';
|
||||||
const homeTimelineController: AppController = async (c) => {
|
const homeTimelineController: AppController = async (c) => {
|
||||||
const params = c.get('pagination');
|
const params = c.get('pagination');
|
||||||
const pubkey = await c.get('signer')?.getPublicKey()!;
|
const pubkey = await c.get('signer')?.getPublicKey()!;
|
||||||
const authors = await getFeedPubkeys(pubkey);
|
const authors = [...await getFeedPubkeys(pubkey)];
|
||||||
return renderStatuses(c, [{ authors, kinds: [1, 6], ...params }]);
|
return renderStatuses(c, [{ authors, kinds: [1, 6], ...params }]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ export class DittoDB {
|
||||||
switch (protocol) {
|
switch (protocol) {
|
||||||
case 'file:':
|
case 'file:':
|
||||||
case 'memory:':
|
case 'memory:':
|
||||||
return DittoPglite.create(databaseUrl);
|
return DittoPglite.create(databaseUrl, opts);
|
||||||
case 'postgres:':
|
case 'postgres:':
|
||||||
case 'postgresql:':
|
case 'postgresql:':
|
||||||
return DittoPostgres.create(databaseUrl, opts);
|
return DittoPostgres.create(databaseUrl, opts);
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,5 @@ export interface DittoDatabase {
|
||||||
|
|
||||||
export interface DittoDatabaseOpts {
|
export interface DittoDatabaseOpts {
|
||||||
poolSize?: number;
|
poolSize?: number;
|
||||||
|
debug?: 0 | 1 | 2 | 3 | 4 | 5;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,18 @@ import { pg_trgm } from '@electric-sql/pglite/contrib/pg_trgm';
|
||||||
import { PgliteDialect } from '@soapbox/kysely-pglite';
|
import { PgliteDialect } from '@soapbox/kysely-pglite';
|
||||||
import { Kysely } from 'kysely';
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
import { DittoDatabase } from '@/db/DittoDatabase.ts';
|
import { DittoDatabase, DittoDatabaseOpts } from '@/db/DittoDatabase.ts';
|
||||||
import { DittoTables } from '@/db/DittoTables.ts';
|
import { DittoTables } from '@/db/DittoTables.ts';
|
||||||
import { KyselyLogger } from '@/db/KyselyLogger.ts';
|
import { KyselyLogger } from '@/db/KyselyLogger.ts';
|
||||||
|
|
||||||
export class DittoPglite {
|
export class DittoPglite {
|
||||||
static create(databaseUrl: string): DittoDatabase {
|
static create(databaseUrl: string, opts?: DittoDatabaseOpts): DittoDatabase {
|
||||||
const kysely = new Kysely<DittoTables>({
|
const kysely = new Kysely<DittoTables>({
|
||||||
dialect: new PgliteDialect({
|
dialect: new PgliteDialect({
|
||||||
database: new PGlite(databaseUrl, { extensions: { pg_trgm } }),
|
database: new PGlite(databaseUrl, {
|
||||||
|
extensions: { pg_trgm },
|
||||||
|
debug: opts?.debug,
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
log: KyselyLogger,
|
log: KyselyLogger,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,14 @@ async function handleEvent(event: DittoEvent, signal: AbortSignal): Promise<void
|
||||||
if (!(await verifyEventWorker(event))) return;
|
if (!(await verifyEventWorker(event))) return;
|
||||||
if (encounterEvent(event)) return;
|
if (encounterEvent(event)) return;
|
||||||
if (await existsInDB(event)) return;
|
if (await existsInDB(event)) return;
|
||||||
|
|
||||||
debug(`NostrEvent<${event.kind}> ${event.id}`);
|
debug(`NostrEvent<${event.kind}> ${event.id}`);
|
||||||
pipelineEventsCounter.inc({ kind: event.kind });
|
pipelineEventsCounter.inc({ kind: event.kind });
|
||||||
|
|
||||||
|
if (isProtectedEvent(event)) {
|
||||||
|
throw new RelayError('invalid', 'protected event');
|
||||||
|
}
|
||||||
|
|
||||||
if (event.kind !== 24133) {
|
if (event.kind !== 24133) {
|
||||||
await policyFilter(event);
|
await policyFilter(event);
|
||||||
}
|
}
|
||||||
|
|
@ -103,6 +108,11 @@ async function existsInDB(event: DittoEvent): Promise<boolean> {
|
||||||
return events.length > 0;
|
return events.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Check whether the event has a NIP-70 `-` tag. */
|
||||||
|
function isProtectedEvent(event: NostrEvent): boolean {
|
||||||
|
return event.tags.some(([name]) => name === '-');
|
||||||
|
}
|
||||||
|
|
||||||
/** Hydrate the event with the user, if applicable. */
|
/** Hydrate the event with the user, if applicable. */
|
||||||
async function hydrateEvent(event: DittoEvent, signal: AbortSignal): Promise<void> {
|
async function hydrateEvent(event: DittoEvent, signal: AbortSignal): Promise<void> {
|
||||||
await hydrateEvents({ events: [event], store: await Storages.db(), signal });
|
await hydrateEvents({ events: [event], store: await Storages.db(), signal });
|
||||||
|
|
|
||||||
|
|
@ -56,16 +56,16 @@ const getFollows = async (pubkey: string, signal?: AbortSignal): Promise<NostrEv
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Get pubkeys the user follows. */
|
/** Get pubkeys the user follows. */
|
||||||
async function getFollowedPubkeys(pubkey: string, signal?: AbortSignal): Promise<string[]> {
|
async function getFollowedPubkeys(pubkey: string, signal?: AbortSignal): Promise<Set<string>> {
|
||||||
const event = await getFollows(pubkey, signal);
|
const event = await getFollows(pubkey, signal);
|
||||||
if (!event) return [];
|
if (!event) return new Set();
|
||||||
return [...getTagSet(event.tags, 'p')];
|
return getTagSet(event.tags, 'p');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get pubkeys the user follows, including the user's own pubkey. */
|
/** Get pubkeys the user follows, including the user's own pubkey. */
|
||||||
async function getFeedPubkeys(pubkey: string): Promise<string[]> {
|
async function getFeedPubkeys(pubkey: string): Promise<Set<string>> {
|
||||||
const authors = await getFollowedPubkeys(pubkey);
|
const authors = await getFollowedPubkeys(pubkey);
|
||||||
return [...authors, pubkey];
|
return authors.add(pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAncestors(store: NStore, event: NostrEvent, result: NostrEvent[] = []): Promise<NostrEvent[]> {
|
async function getAncestors(store: NStore, event: NostrEvent, result: NostrEvent[] = []): Promise<NostrEvent[]> {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,10 @@ export class Storages {
|
||||||
public static async database(): Promise<DittoDatabase> {
|
public static async database(): Promise<DittoDatabase> {
|
||||||
if (!this._database) {
|
if (!this._database) {
|
||||||
this._database = (async () => {
|
this._database = (async () => {
|
||||||
const db = DittoDB.create(Conf.databaseUrl, { poolSize: Conf.pg.poolSize });
|
const db = DittoDB.create(Conf.databaseUrl, {
|
||||||
|
poolSize: Conf.pg.poolSize,
|
||||||
|
debug: Conf.pgliteDebug,
|
||||||
|
});
|
||||||
await DittoDB.migrate(db.kysely);
|
await DittoDB.migrate(db.kysely);
|
||||||
return db;
|
return db;
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue