diff --git a/packages/ditto/app.ts b/packages/ditto/app.ts index 5c07a250..c01a12ff 100644 --- a/packages/ditto/app.ts +++ b/packages/ditto/app.ts @@ -139,7 +139,11 @@ 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'; +<<<<<<< HEAD import { requireProof, requireRole } from '@/middleware/auth98Middleware.ts'; +======= +import { auth98Middleware, requireProof, requireRole } from '@/middleware/auth98Middleware.ts'; +>>>>>>> origin/main import { cacheControlMiddleware } from '@/middleware/cacheControlMiddleware.ts'; import { cspMiddleware } from '@/middleware/cspMiddleware.ts'; import { metricsMiddleware } from '@/middleware/metricsMiddleware.ts'; diff --git a/packages/ditto/controllers/api/streaming.ts b/packages/ditto/controllers/api/streaming.ts index 0cfeda3e..705a801b 100644 --- a/packages/ditto/controllers/api/streaming.ts +++ b/packages/ditto/controllers/api/streaming.ts @@ -1,4 +1,5 @@ import { DittoTables } from '@ditto/db'; +import { MuteListPolicy } from '@ditto/policies'; import { streamingClientMessagesCounter, streamingConnectionsGauge, @@ -11,7 +12,6 @@ import { Kysely } from 'kysely'; import { z } from 'zod'; import { type AppController } from '@/app.ts'; -import { MuteListPolicy } from '../../../policies/MuteListPolicy.ts'; import { getFeedPubkeys } from '@/queries.ts'; import { AdminStore } from '@/storages/AdminStore.ts'; import { hydrateEvents } from '@/storages/hydrate.ts'; diff --git a/packages/ditto/controllers/nostr/relay.ts b/packages/ditto/controllers/nostr/relay.ts index b1791a80..d55a4fbf 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 { @@ -19,9 +20,6 @@ import { relayInfoController } from '@/controllers/nostr/relay-info.ts'; import { RelayError } from '@/RelayError.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'; import { DittoPipeline } from '@/DittoPipeline.ts'; import { EventsDB } from '@/storages/EventsDB.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/middleware/uploaderMiddleware.ts b/packages/ditto/middleware/uploaderMiddleware.ts index ba0720dd..75ccf2b5 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/test.ts b/packages/ditto/test.ts index 5d0b714c..c96fd0c3 100644 --- a/packages/ditto/test.ts +++ b/packages/ditto/test.ts @@ -1,7 +1,5 @@ import { DittoConf } from '@ditto/conf'; import { DittoDB } from '@ditto/db'; -import ISO6391, { LanguageCode } from 'iso-639-1'; -import lande from 'lande'; import { NostrEvent } from '@nostrify/nostrify'; import { generateSecretKey, nip19 } from 'nostr-tools'; @@ -65,15 +63,3 @@ export function testConf(): DittoConf { 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/lang/deno.json b/packages/lang/deno.json index 8b98df9f..f192fb0f 100644 --- a/packages/lang/deno.json +++ b/packages/lang/deno.json @@ -1,5 +1,6 @@ { "name": "@ditto/lang", + "version": "1.1.0", "exports": { ".": "./language.ts" } diff --git a/packages/lang/language.test.ts b/packages/lang/language.test.ts index fad12092..09dbb66a 100644 --- a/packages/lang/language.test.ts +++ b/packages/lang/language.test.ts @@ -1,6 +1,7 @@ -import { detectLanguage } from './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/policies/MuteListPolicy.test.ts b/packages/policies/MuteListPolicy.test.ts index 1da738f6..d07c4472 100644 --- a/packages/policies/MuteListPolicy.test.ts +++ b/packages/policies/MuteListPolicy.test.ts @@ -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/policies/MuteListPolicy.ts b/packages/policies/MuteListPolicy.ts index 2b3dc82e..d880c57d 100644 --- a/packages/policies/MuteListPolicy.ts +++ b/packages/policies/MuteListPolicy.ts @@ -1,13 +1,18 @@ -import { NostrEvent, NostrRelayOK, NPolicy, NStore } from '@nostrify/nostrify'; - -import { getTagSet } from '@ditto/utils/tags'; +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 index 16cafe18..ca190883 100644 --- a/packages/policies/deno.json +++ b/packages/policies/deno.json @@ -1,5 +1,6 @@ { "name": "@ditto/policies", + "version": "1.1.0", "exports": { ".": "./mod.ts" } diff --git a/packages/ratelimiter/deno.json b/packages/ratelimiter/deno.json index ba0fa4da..66e97171 100644 --- a/packages/ratelimiter/deno.json +++ b/packages/ratelimiter/deno.json @@ -1,5 +1,6 @@ { "name": "@ditto/ratelimiter", + "version": "1.1.0", "exports": { ".": "./mod.ts" } diff --git a/packages/translators/DeepLTranslator.test.ts b/packages/translators/DeepLTranslator.test.ts index 3006e966..ae1565c9 100644 --- a/packages/translators/DeepLTranslator.test.ts +++ b/packages/translators/DeepLTranslator.test.ts @@ -1,8 +1,8 @@ import { DittoConf } from '@ditto/conf'; +import { detectLanguage } from '@ditto/lang'; import { assert, assertEquals } from '@std/assert'; -import { DeepLTranslator } from '@/translators/DeepLTranslator.ts'; -import { getLanguage } from '@/test.ts'; +import { DeepLTranslator } from './DeepLTranslator.ts'; const { deeplBaseUrl: baseUrl, @@ -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/translators/DeepLTranslator.ts b/packages/translators/DeepLTranslator.ts index d1cefaaa..f4b6f918 100644 --- a/packages/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' */ @@ -31,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.test.ts b/packages/translators/LibreTranslateTranslator.test.ts index 47253328..fc6c0a55 100644 --- a/packages/translators/LibreTranslateTranslator.test.ts +++ b/packages/translators/LibreTranslateTranslator.test.ts @@ -1,8 +1,8 @@ import { DittoConf } from '@ditto/conf'; +import { detectLanguage } from '@ditto/lang'; import { assertEquals } from '@std/assert'; -import { LibreTranslateTranslator } from '@/translators/LibreTranslateTranslator.ts'; -import { getLanguage } from '@/test.ts'; +import { LibreTranslateTranslator } from './LibreTranslateTranslator.ts'; const { libretranslateBaseUrl: baseUrl, @@ -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/translators/LibreTranslateTranslator.ts b/packages/translators/LibreTranslateTranslator.ts index ef7fb1f8..b75f9b54 100644 --- a/packages/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' */ @@ -31,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 })), ); diff --git a/packages/translators/mod.ts b/packages/translators/mod.ts index df177bd6..e60f19c7 100644 --- a/packages/translators/mod.ts +++ b/packages/translators/mod.ts @@ -1,5 +1,4 @@ export { DeepLTranslator } from './DeepLTranslator.ts'; export { LibreTranslateTranslator } from './LibreTranslateTranslator.ts'; -export type { LanguageCode } from 'iso-639-1'; 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' }, +); diff --git a/packages/uploaders/DenoUploader.ts b/packages/uploaders/DenoUploader.ts index fd30d8c6..a97bdb52 100644 --- a/packages/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/uploaders/IPFSUploader.ts b/packages/uploaders/IPFSUploader.ts index 7bf5165b..cf9c1516 100644 --- a/packages/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/uploaders/S3Uploader.ts b/packages/uploaders/S3Uploader.ts index c0d776f8..551a554d 100644 --- a/packages/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 index 3e0bde0a..b37b8aa7 100644 --- a/packages/uploaders/deno.json +++ b/packages/uploaders/deno.json @@ -1,5 +1,6 @@ { "name": "@ditto/uploaders", + "version": "1.1.0", "exports": { ".": "./mod.ts" }