From b2cd5c541b021b46837fb4a8c157ea9d0dcb2daa Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 18 Feb 2025 12:42:55 -0600 Subject: [PATCH 1/6] Move uploaders into @ditto/uploaders --- deno.json | 3 ++- packages/ditto/middleware/uploaderMiddleware.ts | 4 +--- packages/{ditto => }/uploaders/DenoUploader.ts | 3 ++- packages/{ditto => }/uploaders/IPFSUploader.ts | 3 ++- packages/{ditto => }/uploaders/S3Uploader.ts | 3 ++- packages/uploaders/deno.json | 7 +++++++ packages/uploaders/mod.ts | 3 +++ 7 files changed, 19 insertions(+), 7 deletions(-) rename packages/{ditto => }/uploaders/DenoUploader.ts (95%) rename packages/{ditto => }/uploaders/IPFSUploader.ts (96%) rename packages/{ditto => }/uploaders/S3Uploader.ts (96%) create mode 100644 packages/uploaders/deno.json create mode 100644 packages/uploaders/mod.ts diff --git a/deno.json b/deno.json index 412f32a3..70b8cd74 100644 --- a/deno.json +++ b/deno.json @@ -5,7 +5,8 @@ "./packages/conf", "./packages/db", "./packages/ditto", - "./packages/metrics" + "./packages/metrics", + "./packages/uploaders" ], "tasks": { "start": "deno run -A --env-file --deny-read=.env packages/ditto/server.ts", diff --git a/packages/ditto/middleware/uploaderMiddleware.ts b/packages/ditto/middleware/uploaderMiddleware.ts index 056106c1..10cd3d2b 100644 --- a/packages/ditto/middleware/uploaderMiddleware.ts +++ b/packages/ditto/middleware/uploaderMiddleware.ts @@ -1,10 +1,8 @@ +import { DenoUploader, IPFSUploader, S3Uploader } from '@ditto/uploaders'; import { BlossomUploader, NostrBuildUploader } from '@nostrify/nostrify/uploaders'; import { safeFetch } from '@soapbox/safe-fetch'; import { AppMiddleware } from '@/app.ts'; -import { DenoUploader } from '@/uploaders/DenoUploader.ts'; -import { IPFSUploader } from '@/uploaders/IPFSUploader.ts'; -import { S3Uploader } from '@/uploaders/S3Uploader.ts'; /** Set an uploader for the user. */ export const uploaderMiddleware: AppMiddleware = async (c, next) => { diff --git a/packages/ditto/uploaders/DenoUploader.ts b/packages/uploaders/DenoUploader.ts similarity index 95% rename from packages/ditto/uploaders/DenoUploader.ts rename to packages/uploaders/DenoUploader.ts index fd30d8c6..a97bdb52 100644 --- a/packages/ditto/uploaders/DenoUploader.ts +++ b/packages/uploaders/DenoUploader.ts @@ -1,10 +1,11 @@ import { join } from 'node:path'; -import { NUploader } from '@nostrify/nostrify'; import { crypto } from '@std/crypto'; import { encodeHex } from '@std/encoding/hex'; import { extensionsByType } from '@std/media-types'; +import type { NUploader } from '@nostrify/nostrify'; + export interface DenoUploaderOpts { baseUrl: string; dir: string; diff --git a/packages/ditto/uploaders/IPFSUploader.ts b/packages/uploaders/IPFSUploader.ts similarity index 96% rename from packages/ditto/uploaders/IPFSUploader.ts rename to packages/uploaders/IPFSUploader.ts index 7bf5165b..cf9c1516 100644 --- a/packages/ditto/uploaders/IPFSUploader.ts +++ b/packages/uploaders/IPFSUploader.ts @@ -1,6 +1,7 @@ -import { NUploader } from '@nostrify/nostrify'; import { z } from 'zod'; +import type { NUploader } from '@nostrify/nostrify'; + export interface IPFSUploaderOpts { baseUrl: string; apiUrl?: string; diff --git a/packages/ditto/uploaders/S3Uploader.ts b/packages/uploaders/S3Uploader.ts similarity index 96% rename from packages/ditto/uploaders/S3Uploader.ts rename to packages/uploaders/S3Uploader.ts index c0d776f8..551a554d 100644 --- a/packages/ditto/uploaders/S3Uploader.ts +++ b/packages/uploaders/S3Uploader.ts @@ -1,11 +1,12 @@ import { join } from 'node:path'; import { S3Client } from '@bradenmacdonald/s3-lite-client'; -import { NUploader } from '@nostrify/nostrify'; import { crypto } from '@std/crypto'; import { encodeHex } from '@std/encoding/hex'; import { extensionsByType } from '@std/media-types'; +import type { NUploader } from '@nostrify/nostrify'; + export interface S3UploaderOpts { endPoint: string; region: string; diff --git a/packages/uploaders/deno.json b/packages/uploaders/deno.json new file mode 100644 index 00000000..b37b8aa7 --- /dev/null +++ b/packages/uploaders/deno.json @@ -0,0 +1,7 @@ +{ + "name": "@ditto/uploaders", + "version": "1.1.0", + "exports": { + ".": "./mod.ts" + } +} diff --git a/packages/uploaders/mod.ts b/packages/uploaders/mod.ts new file mode 100644 index 00000000..c5405344 --- /dev/null +++ b/packages/uploaders/mod.ts @@ -0,0 +1,3 @@ +export { DenoUploader } from './DenoUploader.ts'; +export { IPFSUploader } from './IPFSUploader.ts'; +export { S3Uploader } from './S3Uploader.ts'; From d901a722e56baa54fde14c5f5b9a917c08b78b85 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 18 Feb 2025 12:48:27 -0600 Subject: [PATCH 2/6] Make @ditto/lang its own package --- deno.json | 1 + packages/ditto/storages/EventsDB.ts | 2 +- packages/lang/deno.json | 7 +++++++ packages/{ditto/utils => lang}/language.test.ts | 3 ++- packages/{ditto/utils => lang}/language.ts | 0 5 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 packages/lang/deno.json rename packages/{ditto/utils => lang}/language.test.ts (98%) rename packages/{ditto/utils => lang}/language.ts (100%) diff --git a/deno.json b/deno.json index 70b8cd74..33c8e1d6 100644 --- a/deno.json +++ b/deno.json @@ -5,6 +5,7 @@ "./packages/conf", "./packages/db", "./packages/ditto", + "./packages/lang", "./packages/metrics", "./packages/uploaders" ], diff --git a/packages/ditto/storages/EventsDB.ts b/packages/ditto/storages/EventsDB.ts index 622f5811..e7669861 100644 --- a/packages/ditto/storages/EventsDB.ts +++ b/packages/ditto/storages/EventsDB.ts @@ -1,6 +1,7 @@ // deno-lint-ignore-file require-await import { DittoTables } from '@ditto/db'; +import { detectLanguage } from '@ditto/lang'; import { NPostgres, NPostgresSchema } from '@nostrify/db'; import { dbEventsCounter } from '@ditto/metrics'; import { NIP50, NKinds, NostrEvent, NostrFilter, NSchema as n } from '@nostrify/nostrify'; @@ -18,7 +19,6 @@ import { isNostrId } from '@/utils.ts'; import { abortError } from '@/utils/abort.ts'; import { purifyEvent } from '@/utils/purify.ts'; import { DittoEvent } from '@/interfaces/DittoEvent.ts'; -import { detectLanguage } from '@/utils/language.ts'; import { getMediaLinks } from '@/utils/note.ts'; /** Function to decide whether or not to index a tag. */ diff --git a/packages/lang/deno.json b/packages/lang/deno.json new file mode 100644 index 00000000..f192fb0f --- /dev/null +++ b/packages/lang/deno.json @@ -0,0 +1,7 @@ +{ + "name": "@ditto/lang", + "version": "1.1.0", + "exports": { + ".": "./language.ts" + } +} diff --git a/packages/ditto/utils/language.test.ts b/packages/lang/language.test.ts similarity index 98% rename from packages/ditto/utils/language.test.ts rename to packages/lang/language.test.ts index 66a26edd..09dbb66a 100644 --- a/packages/ditto/utils/language.test.ts +++ b/packages/lang/language.test.ts @@ -1,6 +1,7 @@ -import { detectLanguage } from '@/utils/language.ts'; import { assertEquals } from '@std/assert'; +import { detectLanguage } from './language.ts'; + Deno.test('Detect English language', () => { assertEquals(detectLanguage(``, 0.90), undefined); assertEquals(detectLanguage(`Good morning my fellow friends`, 0.90), 'en'); diff --git a/packages/ditto/utils/language.ts b/packages/lang/language.ts similarity index 100% rename from packages/ditto/utils/language.ts rename to packages/lang/language.ts From 990646da26cbd266a1afe1463b66a3ca696bf9b9 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 18 Feb 2025 13:00:49 -0600 Subject: [PATCH 3/6] Make @ditto/translators its own package --- deno.json | 1 + packages/ditto/app.ts | 2 +- .../ditto/middleware/translatorMiddleware.ts | 3 +-- packages/ditto/test.ts | 14 ------------- .../translators/DeepLTranslator.test.ts | 20 +++++++++---------- .../translators/DeepLTranslator.ts | 7 ++++--- .../DittoTranslator.ts | 0 .../LibreTranslateTranslator.test.ts | 20 +++++++++---------- .../translators/LibreTranslateTranslator.ts | 7 ++++--- packages/translators/deno.json | 7 +++++++ packages/translators/mod.ts | 4 ++++ packages/translators/schema.ts | 8 ++++++++ 12 files changed, 50 insertions(+), 43 deletions(-) rename packages/{ditto => }/translators/DeepLTranslator.test.ts (79%) rename packages/{ditto => }/translators/DeepLTranslator.ts (93%) rename packages/{ditto/interfaces => translators}/DittoTranslator.ts (100%) rename packages/{ditto => }/translators/LibreTranslateTranslator.test.ts (70%) rename packages/{ditto => }/translators/LibreTranslateTranslator.ts (94%) create mode 100644 packages/translators/deno.json create mode 100644 packages/translators/mod.ts create mode 100644 packages/translators/schema.ts diff --git a/deno.json b/deno.json index 33c8e1d6..888db8cf 100644 --- a/deno.json +++ b/deno.json @@ -7,6 +7,7 @@ "./packages/ditto", "./packages/lang", "./packages/metrics", + "./packages/translators", "./packages/uploaders" ], "tasks": { diff --git a/packages/ditto/app.ts b/packages/ditto/app.ts index 6a54f66f..88bfa7f9 100644 --- a/packages/ditto/app.ts +++ b/packages/ditto/app.ts @@ -1,6 +1,7 @@ import { confMw } from '@ditto/api/middleware'; import { type DittoConf } from '@ditto/conf'; import { DittoTables } from '@ditto/db'; +import { type DittoTranslator } from '@ditto/translators'; import { type Context, Env as HonoEnv, Handler, Hono, Input as HonoInput, MiddlewareHandler } from '@hono/hono'; import { every } from '@hono/hono/combine'; import { cors } from '@hono/hono/cors'; @@ -134,7 +135,6 @@ import { metricsController } from '@/controllers/metrics.ts'; import { manifestController } from '@/controllers/manifest.ts'; import { nodeInfoController, nodeInfoSchemaController } from '@/controllers/well-known/nodeinfo.ts'; import { nostrController } from '@/controllers/well-known/nostr.ts'; -import { DittoTranslator } from '@/interfaces/DittoTranslator.ts'; import { auth98Middleware, requireProof, requireRole } from '@/middleware/auth98Middleware.ts'; import { cacheControlMiddleware } from '@/middleware/cacheControlMiddleware.ts'; import { cspMiddleware } from '@/middleware/cspMiddleware.ts'; diff --git a/packages/ditto/middleware/translatorMiddleware.ts b/packages/ditto/middleware/translatorMiddleware.ts index eb97ae44..478c2fb9 100644 --- a/packages/ditto/middleware/translatorMiddleware.ts +++ b/packages/ditto/middleware/translatorMiddleware.ts @@ -1,8 +1,7 @@ +import { DeepLTranslator, LibreTranslateTranslator } from '@ditto/translators'; import { safeFetch } from '@soapbox/safe-fetch'; import { AppMiddleware } from '@/app.ts'; -import { DeepLTranslator } from '@/translators/DeepLTranslator.ts'; -import { LibreTranslateTranslator } from '@/translators/LibreTranslateTranslator.ts'; /** Set the translator used for translating posts. */ export const translatorMiddleware: AppMiddleware = async (c, next) => { diff --git a/packages/ditto/test.ts b/packages/ditto/test.ts index bc9a6787..47052b8d 100644 --- a/packages/ditto/test.ts +++ b/packages/ditto/test.ts @@ -1,6 +1,4 @@ import { DittoDB } from '@ditto/db'; -import ISO6391, { LanguageCode } from 'iso-639-1'; -import lande from 'lande'; import { NostrEvent } from '@nostrify/nostrify'; import { finalizeEvent, generateSecretKey } from 'nostr-tools'; @@ -68,15 +66,3 @@ export async function createTestDB(opts?: { pure?: boolean }) { export function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } - -export function getLanguage(text: string): LanguageCode | undefined { - const [topResult] = lande(text); - if (topResult) { - const [iso6393] = topResult; - const locale = new Intl.Locale(iso6393); - if (ISO6391.validate(locale.language)) { - return locale.language; - } - } - return; -} diff --git a/packages/ditto/translators/DeepLTranslator.test.ts b/packages/translators/DeepLTranslator.test.ts similarity index 79% rename from packages/ditto/translators/DeepLTranslator.test.ts rename to packages/translators/DeepLTranslator.test.ts index 08f16a66..ae1565c9 100644 --- a/packages/ditto/translators/DeepLTranslator.test.ts +++ b/packages/translators/DeepLTranslator.test.ts @@ -1,14 +1,14 @@ +import { DittoConf } from '@ditto/conf'; +import { detectLanguage } from '@ditto/lang'; import { assert, assertEquals } from '@std/assert'; -import { Conf } from '@/config.ts'; -import { DeepLTranslator } from '@/translators/DeepLTranslator.ts'; -import { getLanguage } from '@/test.ts'; +import { DeepLTranslator } from './DeepLTranslator.ts'; const { deeplBaseUrl: baseUrl, deeplApiKey: apiKey, translationProvider, -} = Conf; +} = new DittoConf(Deno.env); const deepl = 'deepl'; @@ -28,9 +28,9 @@ Deno.test('DeepL translation with source language omitted', { ); assertEquals(data.source_lang, 'pt'); - assertEquals(getLanguage(data.results[0]), 'en'); - assertEquals(getLanguage(data.results[1]), 'en'); - assertEquals(getLanguage(data.results[2]), 'en'); + assertEquals(detectLanguage(data.results[0], 0), 'en'); + assertEquals(detectLanguage(data.results[1], 0), 'en'); + assertEquals(detectLanguage(data.results[2], 0), 'en'); }); Deno.test('DeepL translation with source language set', { @@ -49,9 +49,9 @@ Deno.test('DeepL translation with source language set', { ); assertEquals(data.source_lang, 'pt'); - assertEquals(getLanguage(data.results[0]), 'en'); - assertEquals(getLanguage(data.results[1]), 'en'); - assertEquals(getLanguage(data.results[2]), 'en'); + assertEquals(detectLanguage(data.results[0], 0), 'en'); + assertEquals(detectLanguage(data.results[1], 0), 'en'); + assertEquals(detectLanguage(data.results[2], 0), 'en'); }); Deno.test("DeepL translation doesn't alter Nostr URIs", { diff --git a/packages/ditto/translators/DeepLTranslator.ts b/packages/translators/DeepLTranslator.ts similarity index 93% rename from packages/ditto/translators/DeepLTranslator.ts rename to packages/translators/DeepLTranslator.ts index d1cefaaa..4c077a87 100644 --- a/packages/ditto/translators/DeepLTranslator.ts +++ b/packages/translators/DeepLTranslator.ts @@ -1,8 +1,9 @@ -import { LanguageCode } from 'iso-639-1'; import { z } from 'zod'; -import { DittoTranslator } from '@/interfaces/DittoTranslator.ts'; -import { languageSchema } from '@/schema.ts'; +import { languageSchema } from './schema.ts'; + +import type { LanguageCode } from 'iso-639-1'; +import type { DittoTranslator } from './DittoTranslator.ts'; interface DeepLTranslatorOpts { /** DeepL base URL to use. Default: 'https://api.deepl.com' */ diff --git a/packages/ditto/interfaces/DittoTranslator.ts b/packages/translators/DittoTranslator.ts similarity index 100% rename from packages/ditto/interfaces/DittoTranslator.ts rename to packages/translators/DittoTranslator.ts diff --git a/packages/ditto/translators/LibreTranslateTranslator.test.ts b/packages/translators/LibreTranslateTranslator.test.ts similarity index 70% rename from packages/ditto/translators/LibreTranslateTranslator.test.ts rename to packages/translators/LibreTranslateTranslator.test.ts index edda3039..fc6c0a55 100644 --- a/packages/ditto/translators/LibreTranslateTranslator.test.ts +++ b/packages/translators/LibreTranslateTranslator.test.ts @@ -1,14 +1,14 @@ +import { DittoConf } from '@ditto/conf'; +import { detectLanguage } from '@ditto/lang'; import { assertEquals } from '@std/assert'; -import { Conf } from '@/config.ts'; -import { LibreTranslateTranslator } from '@/translators/LibreTranslateTranslator.ts'; -import { getLanguage } from '@/test.ts'; +import { LibreTranslateTranslator } from './LibreTranslateTranslator.ts'; const { libretranslateBaseUrl: baseUrl, libretranslateApiKey: apiKey, translationProvider, -} = Conf; +} = new DittoConf(Deno.env); const libretranslate = 'libretranslate'; @@ -28,9 +28,9 @@ Deno.test('LibreTranslate translation with source language omitted', { ); assertEquals(data.source_lang, 'pt'); - assertEquals(getLanguage(data.results[0]), 'ca'); - assertEquals(getLanguage(data.results[1]), 'ca'); - assertEquals(getLanguage(data.results[2]), 'ca'); + assertEquals(detectLanguage(data.results[0], 0), 'ca'); + assertEquals(detectLanguage(data.results[1], 0), 'ca'); + assertEquals(detectLanguage(data.results[2], 0), 'ca'); }); Deno.test('LibreTranslate translation with source language set', { @@ -49,7 +49,7 @@ Deno.test('LibreTranslate translation with source language set', { ); assertEquals(data.source_lang, 'pt'); - assertEquals(getLanguage(data.results[0]), 'ca'); - assertEquals(getLanguage(data.results[1]), 'ca'); - assertEquals(getLanguage(data.results[2]), 'ca'); + assertEquals(detectLanguage(data.results[0], 0), 'ca'); + assertEquals(detectLanguage(data.results[1], 0), 'ca'); + assertEquals(detectLanguage(data.results[2], 0), 'ca'); }); diff --git a/packages/ditto/translators/LibreTranslateTranslator.ts b/packages/translators/LibreTranslateTranslator.ts similarity index 94% rename from packages/ditto/translators/LibreTranslateTranslator.ts rename to packages/translators/LibreTranslateTranslator.ts index ef7fb1f8..041a0ee7 100644 --- a/packages/ditto/translators/LibreTranslateTranslator.ts +++ b/packages/translators/LibreTranslateTranslator.ts @@ -1,8 +1,9 @@ -import { LanguageCode } from 'iso-639-1'; import { z } from 'zod'; -import { DittoTranslator } from '@/interfaces/DittoTranslator.ts'; -import { languageSchema } from '@/schema.ts'; +import { languageSchema } from './schema.ts'; + +import type { LanguageCode } from 'iso-639-1'; +import type { DittoTranslator } from './DittoTranslator.ts'; interface LibreTranslateTranslatorOpts { /** Libretranslate endpoint to use. Default: 'https://libretranslate.com' */ diff --git a/packages/translators/deno.json b/packages/translators/deno.json new file mode 100644 index 00000000..5d603f3a --- /dev/null +++ b/packages/translators/deno.json @@ -0,0 +1,7 @@ +{ + "name": "@ditto/translators", + "version": "1.1.0", + "exports": { + ".": "./mod.ts" + } +} diff --git a/packages/translators/mod.ts b/packages/translators/mod.ts new file mode 100644 index 00000000..e60f19c7 --- /dev/null +++ b/packages/translators/mod.ts @@ -0,0 +1,4 @@ +export { DeepLTranslator } from './DeepLTranslator.ts'; +export { LibreTranslateTranslator } from './LibreTranslateTranslator.ts'; + +export type { DittoTranslator } from './DittoTranslator.ts'; diff --git a/packages/translators/schema.ts b/packages/translators/schema.ts new file mode 100644 index 00000000..803ef1b0 --- /dev/null +++ b/packages/translators/schema.ts @@ -0,0 +1,8 @@ +import ISO6391 from 'iso-639-1'; +import z from 'zod'; + +/** Value is a ISO-639-1 language code. */ +export const languageSchema = z.string().refine( + (val) => ISO6391.validate(val), + { message: 'Not a valid language in ISO-639-1 format' }, +); From 025a86fda2c22c77744fe4405fbdc8eb1e91f894 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 18 Feb 2025 13:05:47 -0600 Subject: [PATCH 4/6] translators: add missing return types --- packages/translators/DeepLTranslator.ts | 2 +- packages/translators/LibreTranslateTranslator.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/translators/DeepLTranslator.ts b/packages/translators/DeepLTranslator.ts index 4c077a87..f4b6f918 100644 --- a/packages/translators/DeepLTranslator.ts +++ b/packages/translators/DeepLTranslator.ts @@ -32,7 +32,7 @@ export class DeepLTranslator implements DittoTranslator { source: LanguageCode | undefined, dest: LanguageCode, opts?: { signal?: AbortSignal }, - ) { + ): Promise<{ results: string[]; source_lang: LanguageCode }> { const { translations } = await this.translateMany(texts, source, dest, opts); return { diff --git a/packages/translators/LibreTranslateTranslator.ts b/packages/translators/LibreTranslateTranslator.ts index 041a0ee7..b75f9b54 100644 --- a/packages/translators/LibreTranslateTranslator.ts +++ b/packages/translators/LibreTranslateTranslator.ts @@ -32,7 +32,7 @@ export class LibreTranslateTranslator implements DittoTranslator { source: LanguageCode | undefined, dest: LanguageCode, opts?: { signal?: AbortSignal }, - ) { + ): Promise<{ results: string[]; source_lang: LanguageCode }> { const translations = await Promise.all( texts.map((text) => this.translateOne(text, source, dest, 'html', { signal: opts?.signal })), ); From 6f9081bbafe7532800bcfa761d2dc1d9f00847ac Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 18 Feb 2025 13:13:05 -0600 Subject: [PATCH 5/6] Make @ditto/policies its own package --- deno.json | 1 + packages/ditto/controllers/api/streaming.ts | 2 +- .../policies/MuteListPolicy.test.ts | 41 ++++++++----------- .../{ditto => }/policies/MuteListPolicy.ts | 13 ++++-- packages/policies/deno.json | 7 ++++ packages/policies/mod.ts | 1 + 6 files changed, 37 insertions(+), 28 deletions(-) rename packages/{ditto => }/policies/MuteListPolicy.test.ts (67%) rename packages/{ditto => }/policies/MuteListPolicy.ts (64%) create mode 100644 packages/policies/deno.json create mode 100644 packages/policies/mod.ts diff --git a/deno.json b/deno.json index 888db8cf..0dd5034c 100644 --- a/deno.json +++ b/deno.json @@ -7,6 +7,7 @@ "./packages/ditto", "./packages/lang", "./packages/metrics", + "./packages/policies", "./packages/translators", "./packages/uploaders" ], diff --git a/packages/ditto/controllers/api/streaming.ts b/packages/ditto/controllers/api/streaming.ts index 7f2f8b64..01eaaed8 100644 --- a/packages/ditto/controllers/api/streaming.ts +++ b/packages/ditto/controllers/api/streaming.ts @@ -1,3 +1,4 @@ +import { MuteListPolicy } from '@ditto/policies'; import { streamingClientMessagesCounter, streamingConnectionsGauge, @@ -9,7 +10,6 @@ import { logi } from '@soapbox/logi'; import { z } from 'zod'; import { type AppController } from '@/app.ts'; -import { MuteListPolicy } from '@/policies/MuteListPolicy.ts'; import { getFeedPubkeys } from '@/queries.ts'; import { hydrateEvents } from '@/storages/hydrate.ts'; import { Storages } from '@/storages.ts'; diff --git a/packages/ditto/policies/MuteListPolicy.test.ts b/packages/policies/MuteListPolicy.test.ts similarity index 67% rename from packages/ditto/policies/MuteListPolicy.test.ts rename to packages/policies/MuteListPolicy.test.ts index 89d7d993..d07c4472 100644 --- a/packages/ditto/policies/MuteListPolicy.test.ts +++ b/packages/policies/MuteListPolicy.test.ts @@ -1,8 +1,8 @@ import { MockRelay } from '@nostrify/nostrify/test'; import { assertEquals } from '@std/assert'; -import { UserStore } from '@/storages/UserStore.ts'; -import { MuteListPolicy } from '@/policies/MuteListPolicy.ts'; + +import { MuteListPolicy } from './MuteListPolicy.ts'; import userBlack from '~/fixtures/events/kind-0-black.json' with { type: 'json' }; import userMe from '~/fixtures/events/event-0-makes-repost-with-quote-repost.json' with { type: 'json' }; @@ -16,14 +16,12 @@ Deno.test('block event: muted user cannot post', async () => { const blockEventCopy = structuredClone(blockEvent); const event1authorUserMeCopy = structuredClone(event1authorUserMe); - const db = new MockRelay(); + const relay = new MockRelay(); + const policy = new MuteListPolicy(userBlack.pubkey, relay); - const store = new UserStore(userBlackCopy.pubkey, db); - const policy = new MuteListPolicy(userBlack.pubkey, db); - - await store.event(blockEventCopy); - await store.event(userBlackCopy); - await store.event(userMeCopy); + await relay.event(blockEventCopy); + await relay.event(userBlackCopy); + await relay.event(userMeCopy); const ok = await policy.call(event1authorUserMeCopy); @@ -35,13 +33,11 @@ Deno.test('allow event: user is NOT muted because there is no muted event', asyn const userMeCopy = structuredClone(userMe); const event1authorUserMeCopy = structuredClone(event1authorUserMe); - const db = new MockRelay(); + const relay = new MockRelay(); + const policy = new MuteListPolicy(userBlack.pubkey, relay); - const store = new UserStore(userBlackCopy.pubkey, db); - const policy = new MuteListPolicy(userBlack.pubkey, db); - - await store.event(userBlackCopy); - await store.event(userMeCopy); + await relay.event(userBlackCopy); + await relay.event(userMeCopy); const ok = await policy.call(event1authorUserMeCopy); @@ -55,16 +51,15 @@ Deno.test('allow event: user is NOT muted because he is not in mute event', asyn const blockEventCopy = structuredClone(blockEvent); const event1copy = structuredClone(event1); - const db = new MockRelay(); + const relay = new MockRelay(); - const store = new UserStore(userBlackCopy.pubkey, db); - const policy = new MuteListPolicy(userBlack.pubkey, db); + const policy = new MuteListPolicy(userBlack.pubkey, relay); - await store.event(userBlackCopy); - await store.event(blockEventCopy); - await store.event(userMeCopy); - await store.event(event1copy); - await store.event(event1authorUserMeCopy); + await relay.event(userBlackCopy); + await relay.event(blockEventCopy); + await relay.event(userMeCopy); + await relay.event(event1copy); + await relay.event(event1authorUserMeCopy); const ok = await policy.call(event1copy); diff --git a/packages/ditto/policies/MuteListPolicy.ts b/packages/policies/MuteListPolicy.ts similarity index 64% rename from packages/ditto/policies/MuteListPolicy.ts rename to packages/policies/MuteListPolicy.ts index 130d10df..d880c57d 100644 --- a/packages/ditto/policies/MuteListPolicy.ts +++ b/packages/policies/MuteListPolicy.ts @@ -1,13 +1,18 @@ -import { NostrEvent, NostrRelayOK, NPolicy, NStore } from '@nostrify/nostrify'; - -import { getTagSet } from '@/utils/tags.ts'; +import type { NostrEvent, NostrRelayOK, NPolicy, NStore } from '@nostrify/nostrify'; export class MuteListPolicy implements NPolicy { constructor(private pubkey: string, private store: NStore) {} async call(event: NostrEvent): Promise { + const pubkeys = new Set(); + const [muteList] = await this.store.query([{ authors: [this.pubkey], kinds: [10000], limit: 1 }]); - const pubkeys = getTagSet(muteList?.tags ?? [], 'p'); + + for (const [name, value] of muteList?.tags ?? []) { + if (name === 'p') { + pubkeys.add(value); + } + } if (pubkeys.has(event.pubkey)) { return ['OK', event.id, false, 'blocked: Your account has been deactivated.']; diff --git a/packages/policies/deno.json b/packages/policies/deno.json new file mode 100644 index 00000000..ca190883 --- /dev/null +++ b/packages/policies/deno.json @@ -0,0 +1,7 @@ +{ + "name": "@ditto/policies", + "version": "1.1.0", + "exports": { + ".": "./mod.ts" + } +} diff --git a/packages/policies/mod.ts b/packages/policies/mod.ts new file mode 100644 index 00000000..9748a4cf --- /dev/null +++ b/packages/policies/mod.ts @@ -0,0 +1 @@ +export { MuteListPolicy } from './MuteListPolicy.ts'; From ac3a9fdf5aa3ebd585f9d50aa1082fbb5750e776 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 18 Feb 2025 13:16:42 -0600 Subject: [PATCH 6/6] Make @ditto/ratelimiter its own package --- deno.json | 1 + packages/ditto/controllers/nostr/relay.ts | 4 +--- .../utils => }/ratelimiter/MemoryRateLimiter.test.ts | 0 .../{ditto/utils => }/ratelimiter/MemoryRateLimiter.ts | 3 ++- .../{ditto/utils => }/ratelimiter/MultiRateLimiter.test.ts | 0 packages/{ditto/utils => }/ratelimiter/MultiRateLimiter.ts | 2 +- packages/{ditto/utils => }/ratelimiter/RateLimitError.ts | 2 +- packages/ratelimiter/deno.json | 7 +++++++ packages/ratelimiter/mod.ts | 5 +++++ packages/{ditto/utils => }/ratelimiter/types.ts | 0 10 files changed, 18 insertions(+), 6 deletions(-) rename packages/{ditto/utils => }/ratelimiter/MemoryRateLimiter.test.ts (100%) rename packages/{ditto/utils => }/ratelimiter/MemoryRateLimiter.ts (95%) rename packages/{ditto/utils => }/ratelimiter/MultiRateLimiter.test.ts (100%) rename packages/{ditto/utils => }/ratelimiter/MultiRateLimiter.ts (94%) rename packages/{ditto/utils => }/ratelimiter/RateLimitError.ts (73%) create mode 100644 packages/ratelimiter/deno.json create mode 100644 packages/ratelimiter/mod.ts rename packages/{ditto/utils => }/ratelimiter/types.ts (100%) diff --git a/deno.json b/deno.json index 0dd5034c..2601e28d 100644 --- a/deno.json +++ b/deno.json @@ -8,6 +8,7 @@ "./packages/lang", "./packages/metrics", "./packages/policies", + "./packages/ratelimiter", "./packages/translators", "./packages/uploaders" ], diff --git a/packages/ditto/controllers/nostr/relay.ts b/packages/ditto/controllers/nostr/relay.ts index 92906d04..b4924f22 100644 --- a/packages/ditto/controllers/nostr/relay.ts +++ b/packages/ditto/controllers/nostr/relay.ts @@ -1,5 +1,6 @@ import { type DittoConf } from '@ditto/conf'; import { relayConnectionsGauge, relayEventsCounter, relayMessagesCounter } from '@ditto/metrics'; +import { MemoryRateLimiter, MultiRateLimiter, type RateLimiter } from '@ditto/ratelimiter'; import { logi } from '@soapbox/logi'; import { JsonValue } from '@std/json'; import { @@ -20,9 +21,6 @@ import { RelayError } from '@/RelayError.ts'; import { Storages } from '@/storages.ts'; import { errorJson } from '@/utils/log.ts'; import { purifyEvent } from '@/utils/purify.ts'; -import { MemoryRateLimiter } from '@/utils/ratelimiter/MemoryRateLimiter.ts'; -import { MultiRateLimiter } from '@/utils/ratelimiter/MultiRateLimiter.ts'; -import { RateLimiter } from '@/utils/ratelimiter/types.ts'; import { Time } from '@/utils/time.ts'; /** Limit of initial events returned for a subscription. */ diff --git a/packages/ditto/utils/ratelimiter/MemoryRateLimiter.test.ts b/packages/ratelimiter/MemoryRateLimiter.test.ts similarity index 100% rename from packages/ditto/utils/ratelimiter/MemoryRateLimiter.test.ts rename to packages/ratelimiter/MemoryRateLimiter.test.ts diff --git a/packages/ditto/utils/ratelimiter/MemoryRateLimiter.ts b/packages/ratelimiter/MemoryRateLimiter.ts similarity index 95% rename from packages/ditto/utils/ratelimiter/MemoryRateLimiter.ts rename to packages/ratelimiter/MemoryRateLimiter.ts index 0eaa5540..15546fd0 100644 --- a/packages/ditto/utils/ratelimiter/MemoryRateLimiter.ts +++ b/packages/ratelimiter/MemoryRateLimiter.ts @@ -1,5 +1,6 @@ import { RateLimitError } from './RateLimitError.ts'; -import { RateLimiter, RateLimiterClient } from './types.ts'; + +import type { RateLimiter, RateLimiterClient } from './types.ts'; interface MemoryRateLimiterOpts { limit: number; diff --git a/packages/ditto/utils/ratelimiter/MultiRateLimiter.test.ts b/packages/ratelimiter/MultiRateLimiter.test.ts similarity index 100% rename from packages/ditto/utils/ratelimiter/MultiRateLimiter.test.ts rename to packages/ratelimiter/MultiRateLimiter.test.ts diff --git a/packages/ditto/utils/ratelimiter/MultiRateLimiter.ts b/packages/ratelimiter/MultiRateLimiter.ts similarity index 94% rename from packages/ditto/utils/ratelimiter/MultiRateLimiter.ts rename to packages/ratelimiter/MultiRateLimiter.ts index 14b23142..189ca177 100644 --- a/packages/ditto/utils/ratelimiter/MultiRateLimiter.ts +++ b/packages/ratelimiter/MultiRateLimiter.ts @@ -1,4 +1,4 @@ -import { RateLimiter, RateLimiterClient } from './types.ts'; +import type { RateLimiter, RateLimiterClient } from './types.ts'; export class MultiRateLimiter { constructor(private limiters: RateLimiter[]) {} diff --git a/packages/ditto/utils/ratelimiter/RateLimitError.ts b/packages/ratelimiter/RateLimitError.ts similarity index 73% rename from packages/ditto/utils/ratelimiter/RateLimitError.ts rename to packages/ratelimiter/RateLimitError.ts index ce21af72..da3a4fd8 100644 --- a/packages/ditto/utils/ratelimiter/RateLimitError.ts +++ b/packages/ratelimiter/RateLimitError.ts @@ -1,4 +1,4 @@ -import { RateLimiter, RateLimiterClient } from './types.ts'; +import type { RateLimiter, RateLimiterClient } from './types.ts'; export class RateLimitError extends Error { constructor( diff --git a/packages/ratelimiter/deno.json b/packages/ratelimiter/deno.json new file mode 100644 index 00000000..66e97171 --- /dev/null +++ b/packages/ratelimiter/deno.json @@ -0,0 +1,7 @@ +{ + "name": "@ditto/ratelimiter", + "version": "1.1.0", + "exports": { + ".": "./mod.ts" + } +} diff --git a/packages/ratelimiter/mod.ts b/packages/ratelimiter/mod.ts new file mode 100644 index 00000000..58bbbeaa --- /dev/null +++ b/packages/ratelimiter/mod.ts @@ -0,0 +1,5 @@ +export { MemoryRateLimiter } from './MemoryRateLimiter.ts'; +export { MultiRateLimiter } from './MultiRateLimiter.ts'; +export { RateLimitError } from './RateLimitError.ts'; + +export type { RateLimiter, RateLimiterClient } from './types.ts'; diff --git a/packages/ditto/utils/ratelimiter/types.ts b/packages/ratelimiter/types.ts similarity index 100% rename from packages/ditto/utils/ratelimiter/types.ts rename to packages/ratelimiter/types.ts