mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
Actually push ??
This commit is contained in:
parent
1ed6cac1c4
commit
8823c0987d
6 changed files with 125 additions and 7 deletions
|
|
@ -40,6 +40,7 @@
|
||||||
"@hono/hono": "jsr:@hono/hono@^4.4.6",
|
"@hono/hono": "jsr:@hono/hono@^4.4.6",
|
||||||
"@isaacs/ttlcache": "npm:@isaacs/ttlcache@^1.4.1",
|
"@isaacs/ttlcache": "npm:@isaacs/ttlcache@^1.4.1",
|
||||||
"@lambdalisue/async": "jsr:@lambdalisue/async@^2.1.1",
|
"@lambdalisue/async": "jsr:@lambdalisue/async@^2.1.1",
|
||||||
|
"@negrel/webpush": "jsr:@negrel/webpush@^0.3.0",
|
||||||
"@noble/secp256k1": "npm:@noble/secp256k1@^2.0.0",
|
"@noble/secp256k1": "npm:@noble/secp256k1@^2.0.0",
|
||||||
"@nostrify/db": "jsr:@nostrify/db@^0.35.0",
|
"@nostrify/db": "jsr:@nostrify/db@^0.35.0",
|
||||||
"@nostrify/nostrify": "jsr:@nostrify/nostrify@^0.36.0",
|
"@nostrify/nostrify": "jsr:@nostrify/nostrify@^0.36.0",
|
||||||
|
|
|
||||||
37
deno.lock
generated
37
deno.lock
generated
|
|
@ -22,6 +22,9 @@
|
||||||
"jsr:@gleasonator/policy@0.7.1": "jsr:@gleasonator/policy@0.7.1",
|
"jsr:@gleasonator/policy@0.7.1": "jsr:@gleasonator/policy@0.7.1",
|
||||||
"jsr:@hono/hono@^4.4.6": "jsr:@hono/hono@4.6.2",
|
"jsr:@hono/hono@^4.4.6": "jsr:@hono/hono@4.6.2",
|
||||||
"jsr:@lambdalisue/async@^2.1.1": "jsr:@lambdalisue/async@2.1.1",
|
"jsr:@lambdalisue/async@^2.1.1": "jsr:@lambdalisue/async@2.1.1",
|
||||||
|
"jsr:@negrel/http-ece@0.6.0": "jsr:@negrel/http-ece@0.6.0",
|
||||||
|
"jsr:@negrel/webpush": "jsr:@negrel/webpush@0.3.0",
|
||||||
|
"jsr:@negrel/webpush@^0.3.0": "jsr:@negrel/webpush@0.3.0",
|
||||||
"jsr:@nostrify/db@^0.35.0": "jsr:@nostrify/db@0.35.0",
|
"jsr:@nostrify/db@^0.35.0": "jsr:@nostrify/db@0.35.0",
|
||||||
"jsr:@nostrify/nostrify@^0.22.1": "jsr:@nostrify/nostrify@0.22.5",
|
"jsr:@nostrify/nostrify@^0.22.1": "jsr:@nostrify/nostrify@0.22.5",
|
||||||
"jsr:@nostrify/nostrify@^0.22.4": "jsr:@nostrify/nostrify@0.22.4",
|
"jsr:@nostrify/nostrify@^0.22.4": "jsr:@nostrify/nostrify@0.22.4",
|
||||||
|
|
@ -44,6 +47,7 @@
|
||||||
"jsr:@std/assert@^0.223.0": "jsr:@std/assert@0.223.0",
|
"jsr:@std/assert@^0.223.0": "jsr:@std/assert@0.223.0",
|
||||||
"jsr:@std/assert@^0.224.0": "jsr:@std/assert@0.224.0",
|
"jsr:@std/assert@^0.224.0": "jsr:@std/assert@0.224.0",
|
||||||
"jsr:@std/assert@^0.225.1": "jsr:@std/assert@0.225.3",
|
"jsr:@std/assert@^0.225.1": "jsr:@std/assert@0.225.3",
|
||||||
|
"jsr:@std/bytes@0.224.0": "jsr:@std/bytes@0.224.0",
|
||||||
"jsr:@std/bytes@^0.223.0": "jsr:@std/bytes@0.223.0",
|
"jsr:@std/bytes@^0.223.0": "jsr:@std/bytes@0.223.0",
|
||||||
"jsr:@std/bytes@^0.224.0": "jsr:@std/bytes@0.224.0",
|
"jsr:@std/bytes@^0.224.0": "jsr:@std/bytes@0.224.0",
|
||||||
"jsr:@std/bytes@^1.0.0-rc.3": "jsr:@std/bytes@1.0.0",
|
"jsr:@std/bytes@^1.0.0-rc.3": "jsr:@std/bytes@1.0.0",
|
||||||
|
|
@ -54,6 +58,7 @@
|
||||||
"jsr:@std/crypto@^0.224.0": "jsr:@std/crypto@0.224.0",
|
"jsr:@std/crypto@^0.224.0": "jsr:@std/crypto@0.224.0",
|
||||||
"jsr:@std/dotenv@^0.224.0": "jsr:@std/dotenv@0.224.2",
|
"jsr:@std/dotenv@^0.224.0": "jsr:@std/dotenv@0.224.2",
|
||||||
"jsr:@std/encoding@0.213.1": "jsr:@std/encoding@0.213.1",
|
"jsr:@std/encoding@0.213.1": "jsr:@std/encoding@0.213.1",
|
||||||
|
"jsr:@std/encoding@0.224.0": "jsr:@std/encoding@0.224.0",
|
||||||
"jsr:@std/encoding@1.0.5": "jsr:@std/encoding@1.0.5",
|
"jsr:@std/encoding@1.0.5": "jsr:@std/encoding@1.0.5",
|
||||||
"jsr:@std/encoding@^0.224.0": "jsr:@std/encoding@0.224.3",
|
"jsr:@std/encoding@^0.224.0": "jsr:@std/encoding@0.224.3",
|
||||||
"jsr:@std/encoding@^0.224.1": "jsr:@std/encoding@0.224.3",
|
"jsr:@std/encoding@^0.224.1": "jsr:@std/encoding@0.224.3",
|
||||||
|
|
@ -64,8 +69,10 @@
|
||||||
"jsr:@std/io@^0.223.0": "jsr:@std/io@0.223.0",
|
"jsr:@std/io@^0.223.0": "jsr:@std/io@0.223.0",
|
||||||
"jsr:@std/io@^0.224": "jsr:@std/io@0.224.8",
|
"jsr:@std/io@^0.224": "jsr:@std/io@0.224.8",
|
||||||
"jsr:@std/json@^0.223.0": "jsr:@std/json@0.223.0",
|
"jsr:@std/json@^0.223.0": "jsr:@std/json@0.223.0",
|
||||||
|
"jsr:@std/media-types@0.224.0": "jsr:@std/media-types@0.224.0",
|
||||||
"jsr:@std/media-types@^0.224.1": "jsr:@std/media-types@0.224.1",
|
"jsr:@std/media-types@^0.224.1": "jsr:@std/media-types@0.224.1",
|
||||||
"jsr:@std/path@0.213.1": "jsr:@std/path@0.213.1",
|
"jsr:@std/path@0.213.1": "jsr:@std/path@0.213.1",
|
||||||
|
"jsr:@std/path@0.224.0": "jsr:@std/path@0.224.0",
|
||||||
"jsr:@std/path@1.0.0-rc.1": "jsr:@std/path@1.0.0-rc.1",
|
"jsr:@std/path@1.0.0-rc.1": "jsr:@std/path@1.0.0-rc.1",
|
||||||
"jsr:@std/path@^0.213.1": "jsr:@std/path@0.213.1",
|
"jsr:@std/path@^0.213.1": "jsr:@std/path@0.213.1",
|
||||||
"jsr:@std/streams@^0.223.0": "jsr:@std/streams@0.223.0",
|
"jsr:@std/streams@^0.223.0": "jsr:@std/streams@0.223.0",
|
||||||
|
|
@ -263,6 +270,23 @@
|
||||||
"@lambdalisue/async@2.1.1": {
|
"@lambdalisue/async@2.1.1": {
|
||||||
"integrity": "1fc9bc6f4ed50215cd2f7217842b18cea80f81c25744f88f8c5eb4be5a1c9ab4"
|
"integrity": "1fc9bc6f4ed50215cd2f7217842b18cea80f81c25744f88f8c5eb4be5a1c9ab4"
|
||||||
},
|
},
|
||||||
|
"@negrel/http-ece@0.6.0": {
|
||||||
|
"integrity": "7afdd81b86ea5b21a9677b323c01c3338705e11cc2bfed250870f5349d8f86f7",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/bytes@0.224.0",
|
||||||
|
"jsr:@std/encoding@0.224.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@negrel/webpush@0.3.0": {
|
||||||
|
"integrity": "5200a56e81668f2debadea228fbeabfe0eda2ee85a56786611dd97950bc51b23",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@negrel/http-ece@0.6.0",
|
||||||
|
"jsr:@std/bytes@0.224.0",
|
||||||
|
"jsr:@std/encoding@0.224.0",
|
||||||
|
"jsr:@std/media-types@0.224.0",
|
||||||
|
"jsr:@std/path@0.224.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
"@nostrify/db@0.35.0": {
|
"@nostrify/db@0.35.0": {
|
||||||
"integrity": "637191c41812544e361b7997dc44ea098f8bd7efebb28f37a8a7142a0ecada8d",
|
"integrity": "637191c41812544e361b7997dc44ea098f8bd7efebb28f37a8a7142a0ecada8d",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
|
|
@ -475,6 +499,9 @@
|
||||||
"@std/encoding@0.213.1": {
|
"@std/encoding@0.213.1": {
|
||||||
"integrity": "fcbb6928713dde941a18ca5db88ca1544d0755ec8fb20fe61e2dc8144b390c62"
|
"integrity": "fcbb6928713dde941a18ca5db88ca1544d0755ec8fb20fe61e2dc8144b390c62"
|
||||||
},
|
},
|
||||||
|
"@std/encoding@0.224.0": {
|
||||||
|
"integrity": "efb6dca97d3e9c31392bd5c8cfd9f9fc9decf5a1f4d1f78af7900a493bcf89b5"
|
||||||
|
},
|
||||||
"@std/encoding@0.224.3": {
|
"@std/encoding@0.224.3": {
|
||||||
"integrity": "5e861b6d81be5359fad4155e591acf17c0207b595112d1840998bb9f476dbdaf"
|
"integrity": "5e861b6d81be5359fad4155e591acf17c0207b595112d1840998bb9f476dbdaf"
|
||||||
},
|
},
|
||||||
|
|
@ -564,6 +591,9 @@
|
||||||
"jsr:@std/streams@^0.223.0"
|
"jsr:@std/streams@^0.223.0"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"@std/media-types@0.224.0": {
|
||||||
|
"integrity": "5ac87989393f8cb1c81bee02aef6f5d4c8289b416deabc04f9ad25dff292d0b0"
|
||||||
|
},
|
||||||
"@std/media-types@0.224.1": {
|
"@std/media-types@0.224.1": {
|
||||||
"integrity": "9e69a5daed37c5b5c6d3ce4731dc191f80e67f79bed392b0957d1d03b87f11e1"
|
"integrity": "9e69a5daed37c5b5c6d3ce4731dc191f80e67f79bed392b0957d1d03b87f11e1"
|
||||||
},
|
},
|
||||||
|
|
@ -573,6 +603,12 @@
|
||||||
"jsr:@std/assert@^0.213.1"
|
"jsr:@std/assert@^0.213.1"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"@std/path@0.224.0": {
|
||||||
|
"integrity": "55bca6361e5a6d158b9380e82d4981d82d338ec587de02951e2b7c3a24910ee6",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/assert@^0.224.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
"@std/path@1.0.0-rc.1": {
|
"@std/path@1.0.0-rc.1": {
|
||||||
"integrity": "b8c00ae2f19106a6bb7cbf1ab9be52aa70de1605daeb2dbdc4f87a7cbaf10ff6"
|
"integrity": "b8c00ae2f19106a6bb7cbf1ab9be52aa70de1605daeb2dbdc4f87a7cbaf10ff6"
|
||||||
},
|
},
|
||||||
|
|
@ -2149,6 +2185,7 @@
|
||||||
"jsr:@gfx/canvas-wasm@^0.4.2",
|
"jsr:@gfx/canvas-wasm@^0.4.2",
|
||||||
"jsr:@hono/hono@^4.4.6",
|
"jsr:@hono/hono@^4.4.6",
|
||||||
"jsr:@lambdalisue/async@^2.1.1",
|
"jsr:@lambdalisue/async@^2.1.1",
|
||||||
|
"jsr:@negrel/webpush@^0.3.0",
|
||||||
"jsr:@nostrify/db@^0.35.0",
|
"jsr:@nostrify/db@^0.35.0",
|
||||||
"jsr:@nostrify/nostrify@^0.36.0",
|
"jsr:@nostrify/nostrify@^0.36.0",
|
||||||
"jsr:@nostrify/policies@^0.35.0",
|
"jsr:@nostrify/policies@^0.35.0",
|
||||||
|
|
|
||||||
36
src/DittoPush.ts
Normal file
36
src/DittoPush.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { ApplicationServer, PushMessageOptions, PushSubscriber, PushSubscription } from '@negrel/webpush';
|
||||||
|
|
||||||
|
import { Conf } from '@/config.ts';
|
||||||
|
import { Storages } from '@/storages.ts';
|
||||||
|
import { getInstanceMetadata } from '@/utils/instance.ts';
|
||||||
|
|
||||||
|
export class DittoPush {
|
||||||
|
static _server: Promise<ApplicationServer> | undefined;
|
||||||
|
|
||||||
|
static get server(): Promise<ApplicationServer> {
|
||||||
|
if (!this._server) {
|
||||||
|
this._server = (async () => {
|
||||||
|
const store = await Storages.db();
|
||||||
|
const meta = await getInstanceMetadata(store);
|
||||||
|
|
||||||
|
return await ApplicationServer.new({
|
||||||
|
contactInformation: `mailto:${meta.email}`,
|
||||||
|
vapidKeys: await Conf.vapidKeys,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._server;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async push(
|
||||||
|
subscription: PushSubscription,
|
||||||
|
json: object,
|
||||||
|
opts: PushMessageOptions = {},
|
||||||
|
): Promise<void> {
|
||||||
|
const server = await this.server;
|
||||||
|
const subscriber = new PushSubscriber(server, subscription);
|
||||||
|
const text = JSON.stringify(json);
|
||||||
|
return subscriber.pushTextMessage(text, opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
import ISO6391, { LanguageCode } from 'iso-639-1';
|
import ISO6391, { LanguageCode } from 'iso-639-1';
|
||||||
|
import { generateVapidKeys } from '@negrel/webpush';
|
||||||
import * as dotenv from '@std/dotenv';
|
import * as dotenv from '@std/dotenv';
|
||||||
import { getPublicKey, nip19 } from 'nostr-tools';
|
import { getPublicKey, nip19 } from 'nostr-tools';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
@ -82,6 +83,9 @@ class Conf {
|
||||||
static get pgliteDebug(): 0 | 1 | 2 | 3 | 4 | 5 {
|
static get pgliteDebug(): 0 | 1 | 2 | 3 | 4 | 5 {
|
||||||
return Number(Deno.env.get('PGLITE_DEBUG') || 0) as 0 | 1 | 2 | 3 | 4 | 5;
|
return Number(Deno.env.get('PGLITE_DEBUG') || 0) as 0 | 1 | 2 | 3 | 4 | 5;
|
||||||
}
|
}
|
||||||
|
static get vapidKeys(): Promise<CryptoKeyPair> {
|
||||||
|
return generateVapidKeys(); // FIXME: get the key from environment.
|
||||||
|
}
|
||||||
static db = {
|
static db = {
|
||||||
/** Database query timeout configurations. */
|
/** Database query timeout configurations. */
|
||||||
timeouts: {
|
timeouts: {
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,28 @@ import ISO6391 from 'iso-639-1';
|
||||||
import { Kysely, sql } from 'kysely';
|
import { Kysely, sql } from 'kysely';
|
||||||
import lande from 'lande';
|
import lande from 'lande';
|
||||||
import { LRUCache } from 'lru-cache';
|
import { LRUCache } from 'lru-cache';
|
||||||
|
import { nip19 } from 'nostr-tools';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { Conf } from '@/config.ts';
|
import { Conf } from '@/config.ts';
|
||||||
import { DittoTables } from '@/db/DittoTables.ts';
|
import { DittoTables } from '@/db/DittoTables.ts';
|
||||||
|
import { DittoPush } from '@/DittoPush.ts';
|
||||||
import { DittoEvent } from '@/interfaces/DittoEvent.ts';
|
import { DittoEvent } from '@/interfaces/DittoEvent.ts';
|
||||||
import { pipelineEventsCounter, policyEventsCounter } from '@/metrics.ts';
|
import { pipelineEventsCounter, policyEventsCounter } from '@/metrics.ts';
|
||||||
import { RelayError } from '@/RelayError.ts';
|
import { RelayError } from '@/RelayError.ts';
|
||||||
import { AdminSigner } from '@/signers/AdminSigner.ts';
|
import { AdminSigner } from '@/signers/AdminSigner.ts';
|
||||||
import { hydrateEvents } from '@/storages/hydrate.ts';
|
import { hydrateEvents } from '@/storages/hydrate.ts';
|
||||||
import { Storages } from '@/storages.ts';
|
import { Storages } from '@/storages.ts';
|
||||||
|
import { MastodonPush } from '@/types/MastodonPush.ts';
|
||||||
import { eventAge, parseNip05, Time } from '@/utils.ts';
|
import { eventAge, parseNip05, Time } from '@/utils.ts';
|
||||||
import { policyWorker } from '@/workers/policy.ts';
|
|
||||||
import { verifyEventWorker } from '@/workers/verify.ts';
|
|
||||||
import { getAmount } from '@/utils/bolt11.ts';
|
import { getAmount } from '@/utils/bolt11.ts';
|
||||||
import { nip05Cache } from '@/utils/nip05.ts';
|
import { nip05Cache } from '@/utils/nip05.ts';
|
||||||
import { purifyEvent } from '@/utils/purify.ts';
|
import { purifyEvent } from '@/utils/purify.ts';
|
||||||
import { updateStats } from '@/utils/stats.ts';
|
import { updateStats } from '@/utils/stats.ts';
|
||||||
import { getTagSet } from '@/utils/tags.ts';
|
import { getTagSet } from '@/utils/tags.ts';
|
||||||
|
import { renderNotification } from '@/views/mastodon/notifications.ts';
|
||||||
|
import { policyWorker } from '@/workers/policy.ts';
|
||||||
|
import { verifyEventWorker } from '@/workers/verify.ts';
|
||||||
|
|
||||||
const console = new Stickynotes('ditto:pipeline');
|
const console = new Stickynotes('ditto:pipeline');
|
||||||
|
|
||||||
|
|
@ -72,6 +76,7 @@ async function handleEvent(event: DittoEvent, signal: AbortSignal): Promise<void
|
||||||
} finally {
|
} finally {
|
||||||
await generateSetEvents(event);
|
await generateSetEvents(event);
|
||||||
await streamOut(event);
|
await streamOut(event);
|
||||||
|
await webPush(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -230,8 +235,43 @@ async function streamOut(event: NostrEvent): Promise<void> {
|
||||||
if (isFresh(event)) {
|
if (isFresh(event)) {
|
||||||
const pubsub = await Storages.pubsub();
|
const pubsub = await Storages.pubsub();
|
||||||
await pubsub.event(event);
|
await pubsub.event(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Web Push
|
async function webPush(event: NostrEvent): Promise<void> {
|
||||||
|
if (!isFresh(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const kysely = await Storages.kysely();
|
||||||
|
|
||||||
|
const rows = await kysely
|
||||||
|
.selectFrom('push_subscriptions')
|
||||||
|
.selectAll()
|
||||||
|
.where('pubkey', 'in', [...getTagSet(event.tags, 'p')])
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
for (const row of rows) {
|
||||||
|
const notification = await renderNotification(event, { viewerPubkey: row.pubkey });
|
||||||
|
if (!notification) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const subscription = {
|
||||||
|
endpoint: row.endpoint,
|
||||||
|
keys: {
|
||||||
|
auth: row.auth,
|
||||||
|
p256dh: row.p256dh,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const message: MastodonPush = {
|
||||||
|
notification_id: notification.id,
|
||||||
|
notification_type: notification.type,
|
||||||
|
access_token: nip19.npubEncode(row.pubkey),
|
||||||
|
};
|
||||||
|
|
||||||
|
await DittoPush.push(subscription, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,10 @@
|
||||||
*/
|
*/
|
||||||
export interface MastodonPush {
|
export interface MastodonPush {
|
||||||
access_token: string;
|
access_token: string;
|
||||||
preferred_locale: string;
|
preferred_locale?: string;
|
||||||
notification_id: string;
|
notification_id: string;
|
||||||
notification_type: string;
|
notification_type: string;
|
||||||
icon: string;
|
icon?: string;
|
||||||
title: string;
|
title?: string;
|
||||||
body: string;
|
body?: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue