mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 03:19:46 +00:00
move policy registry into Ditto
This commit is contained in:
parent
d17f117408
commit
e533ec22b6
12 changed files with 790 additions and 58 deletions
|
|
@ -66,7 +66,7 @@
|
|||
"@noble/secp256k1": "npm:@noble/secp256k1@^2.0.0",
|
||||
"@nostrify/db": "jsr:@nostrify/db@^0.39.4",
|
||||
"@nostrify/nostrify": "jsr:@nostrify/nostrify@^0.39.1",
|
||||
"@nostrify/policies": "https://gitlab.com/soapbox-pub/nostrify/-/raw/58c20b3ee23d8be4987d2c0055d86960e7db763d/packages/policies/mod.ts",
|
||||
"@nostrify/policies": "jsr:@nostrify/policies@^0.36.1",
|
||||
"@nostrify/types": "jsr:@nostrify/types@^0.36.0",
|
||||
"@scure/base": "npm:@scure/base@^1.1.6",
|
||||
"@sentry/deno": "https://deno.land/x/sentry@7.112.2/index.mjs",
|
||||
|
|
|
|||
179
deno.lock
generated
179
deno.lock
generated
|
|
@ -4,49 +4,71 @@
|
|||
"jsr:@b-fuze/deno-dom@~0.1.47": "0.1.49",
|
||||
"jsr:@bradenmacdonald/s3-lite-client@~0.7.4": "0.7.6",
|
||||
"jsr:@core/asyncutil@^1.2.0": "1.2.0",
|
||||
"jsr:@denosaurs/plug@1.0.3": "1.0.3",
|
||||
"jsr:@esroyo/scoped-performance@^3.1.0": "3.1.0",
|
||||
"jsr:@gfx/canvas-wasm@~0.4.2": "0.4.2",
|
||||
"jsr:@hono/hono@^4.4.6": "4.7.5",
|
||||
"jsr:@negrel/http-ece@0.6.0": "0.6.0",
|
||||
"jsr:@negrel/webpush@0.3": "0.3.0",
|
||||
"jsr:@nostrify/db@~0.39.4": "0.39.4",
|
||||
"jsr:@nostrify/nostrify@0.36": "0.36.2",
|
||||
"jsr:@nostrify/nostrify@0.39": "0.39.1",
|
||||
"jsr:@nostrify/nostrify@~0.39.1": "0.39.1",
|
||||
"jsr:@nostrify/policies@~0.36.1": "0.36.1",
|
||||
"jsr:@nostrify/types@0.35": "0.35.0",
|
||||
"jsr:@nostrify/types@0.36": "0.36.0",
|
||||
"jsr:@soapbox/kysely-pglite@1": "1.0.0",
|
||||
"jsr:@soapbox/logi@0.3": "0.3.0",
|
||||
"jsr:@soapbox/safe-fetch@2": "2.0.0",
|
||||
"jsr:@std/assert@0.223": "0.223.0",
|
||||
"jsr:@std/assert@0.224": "0.224.0",
|
||||
"jsr:@std/assert@^1.0.12": "1.0.12",
|
||||
"jsr:@std/assert@~0.213.1": "0.213.1",
|
||||
"jsr:@std/assert@~0.225.1": "0.225.3",
|
||||
"jsr:@std/async@^1.0.10": "1.0.12",
|
||||
"jsr:@std/bytes@0.223": "0.223.0",
|
||||
"jsr:@std/bytes@0.224.0": "0.224.0",
|
||||
"jsr:@std/bytes@^1.0.2": "1.0.5",
|
||||
"jsr:@std/cli@0.223": "0.223.0",
|
||||
"jsr:@std/crypto@0.224": "0.224.0",
|
||||
"jsr:@std/data-structures@^1.0.6": "1.0.6",
|
||||
"jsr:@std/encoding@0.213.1": "0.213.1",
|
||||
"jsr:@std/encoding@0.224": "0.224.3",
|
||||
"jsr:@std/encoding@0.224.0": "0.224.0",
|
||||
"jsr:@std/encoding@1.0.5": "1.0.5",
|
||||
"jsr:@std/encoding@~0.224.1": "0.224.3",
|
||||
"jsr:@std/fmt@0.213.1": "0.213.1",
|
||||
"jsr:@std/fs@0.213.1": "0.213.1",
|
||||
"jsr:@std/fs@^1.0.15": "1.0.15",
|
||||
"jsr:@std/fs@~0.229.3": "0.229.3",
|
||||
"jsr:@std/internal@1": "1.0.6",
|
||||
"jsr:@std/internal@^1.0.6": "1.0.6",
|
||||
"jsr:@std/io@0.223": "0.223.0",
|
||||
"jsr:@std/io@0.224": "0.224.9",
|
||||
"jsr:@std/json@0.223": "0.223.0",
|
||||
"jsr:@std/media-types@0.224.0": "0.224.0",
|
||||
"jsr:@std/media-types@~0.224.1": "0.224.1",
|
||||
"jsr:@std/path@0.213.1": "0.213.1",
|
||||
"jsr:@std/path@0.224.0": "0.224.0",
|
||||
"jsr:@std/path@1.0.0-rc.1": "1.0.0-rc.1",
|
||||
"jsr:@std/path@^1.0.8": "1.0.8",
|
||||
"jsr:@std/path@~0.213.1": "0.213.1",
|
||||
"jsr:@std/streams@0.223": "0.223.0",
|
||||
"jsr:@std/testing@^1.0.9": "1.0.10",
|
||||
"npm:@cashu/cashu-ts@^2.2.0": "2.4.1",
|
||||
"npm:@electric-sql/pglite@~0.2.8": "0.2.17",
|
||||
"npm:@isaacs/ttlcache@^1.4.1": "1.4.1",
|
||||
"npm:@noble/secp256k1@2": "2.2.3",
|
||||
"npm:@scure/base@^1.1.6": "1.2.4",
|
||||
"npm:@scure/bip32@^1.4.0": "1.6.2",
|
||||
"npm:@scure/bip39@^1.3.0": "1.5.4",
|
||||
"npm:@types/node@*": "22.5.4",
|
||||
"npm:blurhash@2.0.5": "2.0.5",
|
||||
"npm:comlink-async-generator@^0.0.1": "0.0.1",
|
||||
"npm:comlink@^4.4.1": "4.4.2",
|
||||
"npm:commander@12.1.0": "12.1.0",
|
||||
"npm:entities@^4.5.0": "4.5.0",
|
||||
"npm:fast-stable-stringify@1": "1.0.0",
|
||||
"npm:formdata-helper@0.3": "0.3.0",
|
||||
"npm:hono-rate-limiter@0.3": "0.3.0_hono@4.7.5",
|
||||
"npm:iso-639-1@^3.1.5": "3.1.5",
|
||||
|
|
@ -63,30 +85,45 @@
|
|||
"npm:lru-cache@^10.2.2": "10.4.3",
|
||||
"npm:nostr-tools@2.5.1": "2.5.1",
|
||||
"npm:nostr-tools@^2.10.4": "2.11.0",
|
||||
"npm:nostr-tools@^2.7.0": "2.11.0",
|
||||
"npm:nostr-wasm@0.1": "0.1.0",
|
||||
"npm:path-to-regexp@^7.1.0": "7.2.0",
|
||||
"npm:prom-client@^15.1.2": "15.1.3",
|
||||
"npm:sharp@~0.33.5": "0.33.5",
|
||||
"npm:tldts@^6.0.14": "6.1.85",
|
||||
"npm:tldts@^6.1.61": "6.1.85",
|
||||
"npm:tseep@^1.2.1": "1.3.1",
|
||||
"npm:type-fest@^4.3.0": "4.38.0",
|
||||
"npm:unfurl.js@^6.4.0": "6.4.0",
|
||||
"npm:websocket-ts@^2.1.5": "2.2.1",
|
||||
"npm:websocket-ts@^2.2.1": "2.2.1",
|
||||
"npm:zod@^3.23.8": "3.24.2"
|
||||
},
|
||||
"jsr": {
|
||||
"@b-fuze/deno-dom@0.1.49": {
|
||||
"integrity": "45c40175fdd1e74ab2d4b54c4fdaabbad6e5b76ee28df12a48e076b3fa7901a9"
|
||||
"integrity": "45c40175fdd1e74ab2d4b54c4fdaabbad6e5b76ee28df12a48e076b3fa7901a9",
|
||||
"dependencies": [
|
||||
"jsr:@denosaurs/plug"
|
||||
]
|
||||
},
|
||||
"@bradenmacdonald/s3-lite-client@0.7.6": {
|
||||
"integrity": "2b5976dca95d207dc88e23f9807e3eecbc441b0cf547dcda5784afe6668404d1",
|
||||
"dependencies": [
|
||||
"jsr:@std/io"
|
||||
"jsr:@std/io@0.224"
|
||||
]
|
||||
},
|
||||
"@core/asyncutil@1.2.0": {
|
||||
"integrity": "9967f15190c60df032c13f72ce5ac73d185c34f31c53dc918d8800025854c118"
|
||||
},
|
||||
"@denosaurs/plug@1.0.3": {
|
||||
"integrity": "b010544e386bea0ff3a1d05e0c88f704ea28cbd4d753439c2f1ee021a85d4640",
|
||||
"dependencies": [
|
||||
"jsr:@std/encoding@0.213.1",
|
||||
"jsr:@std/fmt",
|
||||
"jsr:@std/fs@0.213.1",
|
||||
"jsr:@std/path@0.213.1"
|
||||
]
|
||||
},
|
||||
"@esroyo/scoped-performance@3.1.0": {
|
||||
"integrity": "e6a12a1d4edb32cbea7afce005123c1ef684e1815bf9b6caadfbf3e89fe66222"
|
||||
},
|
||||
|
|
@ -113,23 +150,36 @@
|
|||
"jsr:@std/bytes@0.224.0",
|
||||
"jsr:@std/encoding@0.224.0",
|
||||
"jsr:@std/media-types@0.224.0",
|
||||
"jsr:@std/path"
|
||||
"jsr:@std/path@0.224.0"
|
||||
]
|
||||
},
|
||||
"@nostrify/db@0.39.4": {
|
||||
"integrity": "53fecea3b67394cf4f52795f89d1d065bdeb0627b8655cc7fc3a89d6b21adf01",
|
||||
"dependencies": [
|
||||
"jsr:@nostrify/nostrify@0.39",
|
||||
"jsr:@nostrify/types",
|
||||
"jsr:@nostrify/types@0.36",
|
||||
"npm:kysely@~0.27.3",
|
||||
"npm:nostr-tools@^2.10.4"
|
||||
]
|
||||
},
|
||||
"@nostrify/nostrify@0.36.2": {
|
||||
"integrity": "cc4787ca170b623a2e5dfed1baa4426077daa6143af728ea7dd325d58f4d04d6",
|
||||
"dependencies": [
|
||||
"jsr:@nostrify/types@0.35",
|
||||
"jsr:@std/encoding@~0.224.1",
|
||||
"npm:@scure/bip32",
|
||||
"npm:@scure/bip39",
|
||||
"npm:lru-cache@^10.2.0",
|
||||
"npm:nostr-tools@^2.7.0",
|
||||
"npm:websocket-ts@^2.1.5",
|
||||
"npm:zod"
|
||||
]
|
||||
},
|
||||
"@nostrify/nostrify@0.39.1": {
|
||||
"integrity": "84f98c815a07f4151bd02188a3525e438c416e9de632c79c9da9edbfca580d7f",
|
||||
"dependencies": [
|
||||
"jsr:@nostrify/nostrify@~0.39.1",
|
||||
"jsr:@nostrify/types",
|
||||
"jsr:@nostrify/types@0.36",
|
||||
"jsr:@std/crypto",
|
||||
"jsr:@std/encoding@~0.224.1",
|
||||
"npm:@scure/base",
|
||||
|
|
@ -137,10 +187,21 @@
|
|||
"npm:@scure/bip39",
|
||||
"npm:lru-cache@^10.2.0",
|
||||
"npm:nostr-tools@^2.10.4",
|
||||
"npm:websocket-ts",
|
||||
"npm:websocket-ts@^2.2.1",
|
||||
"npm:zod"
|
||||
]
|
||||
},
|
||||
"@nostrify/policies@0.36.1": {
|
||||
"integrity": "6d59af115a687fcd18b6caebab0e4f50ee6cdb0aafa2aacd0aec2065021275b4",
|
||||
"dependencies": [
|
||||
"jsr:@nostrify/nostrify@0.36",
|
||||
"jsr:@nostrify/types@0.35",
|
||||
"npm:nostr-tools@^2.7.0"
|
||||
]
|
||||
},
|
||||
"@nostrify/types@0.35.0": {
|
||||
"integrity": "b8d515563d467072694557d5626fa1600f74e83197eef45dd86a9a99c64f7fe6"
|
||||
},
|
||||
"@nostrify/types@0.36.0": {
|
||||
"integrity": "b3413467debcbd298d217483df4e2aae6c335a34765c90ac7811cf7c637600e7"
|
||||
},
|
||||
|
|
@ -159,6 +220,12 @@
|
|||
"npm:tldts@^6.1.61"
|
||||
]
|
||||
},
|
||||
"@std/assert@0.213.1": {
|
||||
"integrity": "24c28178b30c8e0782c18e8e94ea72b16282207569cdd10ffb9d1d26f2edebfe"
|
||||
},
|
||||
"@std/assert@0.223.0": {
|
||||
"integrity": "eb8d6d879d76e1cc431205bd346ed4d88dc051c6366365b1af47034b0670be24"
|
||||
},
|
||||
"@std/assert@0.224.0": {
|
||||
"integrity": "8643233ec7aec38a940a8264a6e3eed9bfa44e7a71cc6b3c8874213ff401967f"
|
||||
},
|
||||
|
|
@ -180,12 +247,21 @@
|
|||
"@std/async@1.0.12": {
|
||||
"integrity": "d1bfcec459e8012846fe4e38dfc4241ab23240ecda3d8d6dfcf6d81a632e803d"
|
||||
},
|
||||
"@std/bytes@0.223.0": {
|
||||
"integrity": "84b75052cd8680942c397c2631318772b295019098f40aac5c36cead4cba51a8"
|
||||
},
|
||||
"@std/bytes@0.224.0": {
|
||||
"integrity": "a2250e1d0eb7d1c5a426f21267ab9bdeac2447fa87a3d0d1a467d3f7a6058e49"
|
||||
},
|
||||
"@std/bytes@1.0.5": {
|
||||
"integrity": "4465dd739d7963d964c809202ebea6d5c6b8e3829ef25c6a224290fbb8a1021e"
|
||||
},
|
||||
"@std/cli@0.223.0": {
|
||||
"integrity": "2feb7970f2028904c3edc22ea916ce9538113dfc170844f3eae03578c333c356",
|
||||
"dependencies": [
|
||||
"jsr:@std/assert@0.223"
|
||||
]
|
||||
},
|
||||
"@std/crypto@0.224.0": {
|
||||
"integrity": "154ef3ff08ef535562ef1a718718c5b2c5fc3808f0f9100daad69e829bfcdf2d",
|
||||
"dependencies": [
|
||||
|
|
@ -193,6 +269,12 @@
|
|||
"jsr:@std/encoding@0.224"
|
||||
]
|
||||
},
|
||||
"@std/data-structures@1.0.6": {
|
||||
"integrity": "76a7fd8080c66604c0496220a791860492ab21a04a63a969c0b9a0609bbbb760"
|
||||
},
|
||||
"@std/encoding@0.213.1": {
|
||||
"integrity": "fcbb6928713dde941a18ca5db88ca1544d0755ec8fb20fe61e2dc8144b390c62"
|
||||
},
|
||||
"@std/encoding@0.224.0": {
|
||||
"integrity": "efb6dca97d3e9c31392bd5c8cfd9f9fc9decf5a1f4d1f78af7900a493bcf89b5"
|
||||
},
|
||||
|
|
@ -202,12 +284,41 @@
|
|||
"@std/encoding@1.0.5": {
|
||||
"integrity": "ecf363d4fc25bd85bd915ff6733a7e79b67e0e7806334af15f4645c569fefc04"
|
||||
},
|
||||
"@std/fmt@0.213.1": {
|
||||
"integrity": "a06d31777566d874b9c856c10244ac3e6b660bdec4c82506cd46be052a1082c3"
|
||||
},
|
||||
"@std/fs@0.213.1": {
|
||||
"integrity": "fbcaf099f8a85c27ab0712b666262cda8fe6d02e9937bf9313ecaea39a22c501",
|
||||
"dependencies": [
|
||||
"jsr:@std/assert@~0.213.1",
|
||||
"jsr:@std/path@~0.213.1"
|
||||
]
|
||||
},
|
||||
"@std/fs@0.229.3": {
|
||||
"integrity": "783bca21f24da92e04c3893c9e79653227ab016c48e96b3078377ebd5222e6eb",
|
||||
"dependencies": [
|
||||
"jsr:@std/path@1.0.0-rc.1"
|
||||
]
|
||||
},
|
||||
"@std/fs@1.0.15": {
|
||||
"integrity": "c083fb479889d6440d768e498195c3fc499d426fbf9a6592f98f53884d1d3f41",
|
||||
"dependencies": [
|
||||
"jsr:@std/path@^1.0.8"
|
||||
]
|
||||
},
|
||||
"@std/internal@1.0.4": {
|
||||
"integrity": "62e8e4911527e5e4f307741a795c0b0a9e6958d0b3790716ae71ce085f755422"
|
||||
},
|
||||
"@std/internal@1.0.6": {
|
||||
"integrity": "9533b128f230f73bd209408bb07a4b12f8d4255ab2a4d22a1fd6d87304aca9a4"
|
||||
},
|
||||
"@std/io@0.223.0": {
|
||||
"integrity": "2d8c3c2ab3a515619b90da2c6ff5ea7b75a94383259ef4d02116b228393f84f1",
|
||||
"dependencies": [
|
||||
"jsr:@std/assert@0.223",
|
||||
"jsr:@std/bytes@0.223"
|
||||
]
|
||||
},
|
||||
"@std/io@0.224.9": {
|
||||
"integrity": "4414664b6926f665102e73c969cfda06d2c4c59bd5d0c603fd4f1b1c840d6ee3",
|
||||
"dependencies": [
|
||||
|
|
@ -226,14 +337,31 @@
|
|||
"@std/media-types@0.224.1": {
|
||||
"integrity": "9e69a5daed37c5b5c6d3ce4731dc191f80e67f79bed392b0957d1d03b87f11e1"
|
||||
},
|
||||
"@std/path@0.213.1": {
|
||||
"integrity": "f187bf278a172752e02fcbacf6bd78a335ed320d080a7ed3a5a59c3e88abc673",
|
||||
"dependencies": [
|
||||
"jsr:@std/assert@~0.213.1"
|
||||
]
|
||||
},
|
||||
"@std/path@0.224.0": {
|
||||
"integrity": "55bca6361e5a6d158b9380e82d4981d82d338ec587de02951e2b7c3a24910ee6",
|
||||
"dependencies": [
|
||||
"jsr:@std/assert@0.224"
|
||||
]
|
||||
},
|
||||
"@std/path@1.0.0-rc.1": {
|
||||
"integrity": "b8c00ae2f19106a6bb7cbf1ab9be52aa70de1605daeb2dbdc4f87a7cbaf10ff6"
|
||||
},
|
||||
"@std/path@1.0.8": {
|
||||
"integrity": "548fa456bb6a04d3c1a1e7477986b6cffbce95102d0bb447c67c4ee70e0364be"
|
||||
},
|
||||
"@std/streams@0.223.0": {
|
||||
"integrity": "d6b28e498ced3960b04dc5d251f2dcfc1df244b5ec5a48dc23a8f9b490be3b99"
|
||||
"integrity": "d6b28e498ced3960b04dc5d251f2dcfc1df244b5ec5a48dc23a8f9b490be3b99",
|
||||
"dependencies": [
|
||||
"jsr:@std/assert@0.223",
|
||||
"jsr:@std/bytes@0.223",
|
||||
"jsr:@std/io@0.223"
|
||||
]
|
||||
},
|
||||
"@std/testing@1.0.9": {
|
||||
"integrity": "9bdd4ac07cb13e7594ac30e90f6ceef7254ac83a9aeaa089be0008f33aab5cd4"
|
||||
|
|
@ -241,7 +369,11 @@
|
|||
"@std/testing@1.0.10": {
|
||||
"integrity": "8997bd0b0df020b81bf5eae103c66622918adeff7e45e96291c92a29dbf82cc1",
|
||||
"dependencies": [
|
||||
"jsr:@std/assert@^1.0.12"
|
||||
"jsr:@std/assert@^1.0.12",
|
||||
"jsr:@std/data-structures",
|
||||
"jsr:@std/fs@^1.0.15",
|
||||
"jsr:@std/internal@^1.0.6",
|
||||
"jsr:@std/path@^1.0.8"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
@ -429,6 +561,9 @@
|
|||
"@noble/hashes@1.7.1": {
|
||||
"integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ=="
|
||||
},
|
||||
"@noble/secp256k1@2.2.3": {
|
||||
"integrity": "sha512-l7r5oEQym9Us7EAigzg30/PQAvynhMt2uoYtT3t26eGDVm9Yii5mZ5jWSWmZ/oSIR2Et0xfc6DXrG0bZ787V3w=="
|
||||
},
|
||||
"@opentelemetry/api@1.9.0": {
|
||||
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="
|
||||
},
|
||||
|
|
@ -535,6 +670,12 @@
|
|||
"delayed-stream"
|
||||
]
|
||||
},
|
||||
"comlink-async-generator@0.0.1": {
|
||||
"integrity": "sha512-RjOPv6Tb7cL9FiIgwanUJuFG9aW4myAFyyzxZoEkEegeDQrZqr92d1Njv2WIgi7nbGpTiyy5GdNTUubDaNgZ6A==",
|
||||
"dependencies": [
|
||||
"comlink"
|
||||
]
|
||||
},
|
||||
"comlink@4.4.2": {
|
||||
"integrity": "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g=="
|
||||
},
|
||||
|
|
@ -639,6 +780,9 @@
|
|||
"hasown"
|
||||
]
|
||||
},
|
||||
"fast-stable-stringify@1.0.0": {
|
||||
"integrity": "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag=="
|
||||
},
|
||||
"form-data@4.0.2": {
|
||||
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
|
||||
"dependencies": [
|
||||
|
|
@ -989,6 +1133,9 @@
|
|||
"punycode"
|
||||
]
|
||||
},
|
||||
"tseep@1.3.1": {
|
||||
"integrity": "sha512-ZPtfk1tQnZVyr7BPtbJ93qaAh2lZuIOpTMjhrYa4XctT8xe7t4SAW9LIxrySDuYMsfNNayE51E/WNGrNVgVicQ=="
|
||||
},
|
||||
"tslib@2.8.1": {
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
||||
},
|
||||
|
|
@ -1327,6 +1474,9 @@
|
|||
"https://deno.land/std@0.132.0/streams/conversion.ts": "712585bfa0172a97fb68dd46e784ae8ad59d11b88079d6a4ab098ff42e697d21",
|
||||
"https://deno.land/std@0.132.0/testing/_diff.ts": "9d849cd6877694152e01775b2d93f9d6b7aef7e24bfe3bfafc4d7a1ac8e9f392",
|
||||
"https://deno.land/std@0.132.0/testing/asserts.ts": "b0ef969032882b1f7eb1c7571e313214baa1485f7b61cf35807b2434e254365c",
|
||||
"https://deno.land/std@0.80.0/encoding/utf8.ts": "1b7e77db9a12363c67872f8a208886ca1329f160c1ca9133b13d2ed399688b99",
|
||||
"https://deno.land/x/keypress@0.0.7/dep.ts": "feeb0056d332c126343249b79fe86cb0bf3abd03ea4c270cd39575c38d37a911",
|
||||
"https://deno.land/x/keypress@0.0.7/mod.ts": "1130570c2397118a3a301b1137400a8e55486716cc3557b3bd5e9947b6b9c035",
|
||||
"https://deno.land/x/sentry@7.112.2/index.mjs": "04382d5c2f4e233ba389611db46f77943b2a7f6efbeaaf31193f6e586f4366ef",
|
||||
"https://gitlab.com/soapbox-pub/nostrify/-/raw/58c20b3ee23d8be4987d2c0055d86960e7db763d/packages/nostrify/NSchema.ts": "b6f72b3e8a24f8c29f84fea03f5d5dc1926196be751aec598457358846e9b3d5",
|
||||
"https://gitlab.com/soapbox-pub/nostrify/-/raw/58c20b3ee23d8be4987d2c0055d86960e7db763d/packages/policies/AntiDuplicationPolicy.ts": "8586878d762312a326e468ec6de948f3ca7c23bc7fe807ccb7b5dfbd90d3041e",
|
||||
|
|
@ -1387,7 +1537,17 @@
|
|||
"https://gitlab.com/soapbox-pub/postgres.js/-/raw/e79d7d2039446fbf7a37d4eca0d17e94a94b8b53/deno/src/queue.js": "709624843223ea842bf095f6934080f19f1a059a51cbbf82e9827f3bb1bf2ca7",
|
||||
"https://gitlab.com/soapbox-pub/postgres.js/-/raw/e79d7d2039446fbf7a37d4eca0d17e94a94b8b53/deno/src/result.js": "001ff5e0c8d634674f483d07fbcd620a797e3101f842d6c20ca3ace936260465",
|
||||
"https://gitlab.com/soapbox-pub/postgres.js/-/raw/e79d7d2039446fbf7a37d4eca0d17e94a94b8b53/deno/src/subscribe.js": "9e4d0c3e573a6048e77ee2f15abbd5bcd17da9ca85a78c914553472c6d6c169b",
|
||||
"https://gitlab.com/soapbox-pub/postgres.js/-/raw/e79d7d2039446fbf7a37d4eca0d17e94a94b8b53/deno/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed"
|
||||
"https://gitlab.com/soapbox-pub/postgres.js/-/raw/e79d7d2039446fbf7a37d4eca0d17e94a94b8b53/deno/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed",
|
||||
"https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/KeyCombo.ts": "a370b2dca76faa416d00e45479c8ce344971b5b86b44b4d0b213245c4bd2f8a3",
|
||||
"https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/checkbox.ts": "e337ee7396aaefe6cc8c6349a445542fe7f0760311773369c9012b3fa278d21e",
|
||||
"https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/config.ts": "a94a022c757f63ee7c410e29b97d3bfab1811889fb4483f56395cf376a911d1b",
|
||||
"https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/confirm.ts": "dde395f351e14ebff9dd882c49c1376f0c648a5978d17dfad997f9958df01d1c",
|
||||
"https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/input.ts": "5b7a3e194e6a5b74bb26d5ef1363d78619769772ad01244fd6f95b9c66cc6f0d",
|
||||
"https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/list.ts": "3b96403b043b5b5bc417d8d07b34828961c1f9d2bdbc9f24e6ef40fb9c95438e",
|
||||
"https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/mod.ts": "cb10c598652cf7edf600af17f73bcadcdedf6900d9f5b5647e89ba2ea378b7d5",
|
||||
"https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/password.ts": "3c578bd21e4fd283431aa940357f40fed2e26d3de12ad129a696d7fe38ae744d",
|
||||
"https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/text-util.ts": "37c0437d2030c0b6255f10afec7ccfcb6b195e9a0a011bb7956595142c3d7383",
|
||||
"https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/util.ts": "a8285450db7b56a3e507f478aaad68927ecb1ee545449cb869ccc4aace13fada"
|
||||
},
|
||||
"workspace": {
|
||||
"dependencies": [
|
||||
|
|
@ -1400,6 +1560,7 @@
|
|||
"jsr:@negrel/webpush@0.3",
|
||||
"jsr:@nostrify/db@~0.39.4",
|
||||
"jsr:@nostrify/nostrify@~0.39.1",
|
||||
"jsr:@nostrify/policies@~0.36.1",
|
||||
"jsr:@nostrify/types@0.36",
|
||||
"jsr:@soapbox/kysely-pglite@1",
|
||||
"jsr:@soapbox/logi@0.3",
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ import {
|
|||
adminListPoliciesController,
|
||||
adminUpdatePolicyController,
|
||||
} from '@/controllers/api/policies.ts';
|
||||
import { createPolicyEvent, DEFAULT_POLICY_SPEC } from '@/utils/policies.ts';
|
||||
import { createPolicyEvent, DEFAULT_POLICY_SPEC } from '@/utils/policies/mod.ts';
|
||||
|
||||
export interface AppEnv extends DittoEnv {
|
||||
Variables: DittoEnv['Variables'] & {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ const cache = (f: () => Promise<number | undefined>, interval: number) => {
|
|||
logi({
|
||||
level: 'error',
|
||||
ns: 'ditto.routes.instanceV1',
|
||||
message: `Error fetching cached value: ${error}`
|
||||
message: `Error fetching cached value: ${error}`,
|
||||
});
|
||||
value = undefined; // Ensure we retry next time
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { type AppController } from '@/app.ts';
|
||||
import { createPolicyEvent } from '@/utils/policies.ts';
|
||||
import { DEFAULT_POLICY_SPEC, policyRegistry } from '@/utils/policies.ts';
|
||||
import { createPolicyEvent } from '@/utils/policies/mod.ts';
|
||||
import { DEFAULT_POLICY_SPEC, policyRegistry } from '@/utils/policies/mod.ts';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const adminListPoliciesController: AppController = (c) => {
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
import { nostrNow } from '@/utils.ts';
|
||||
import type { DittoConf } from '@ditto/conf';
|
||||
import { PolicyRegistry } from '@nostrify/policies';
|
||||
import { MockRelay } from '@nostrify/nostrify/test';
|
||||
|
||||
type ParamValue = string | number | boolean;
|
||||
|
||||
export const policyRegistry = new PolicyRegistry({
|
||||
antiDuplicationPolicyStore: {
|
||||
get: (key: Deno.KvKey) => Promise.resolve({ key, value: null, versionstamp: null }),
|
||||
set: () => Promise.resolve({ ok: true, versionstamp: '00000000000000000000' }),
|
||||
},
|
||||
store: new MockRelay(),
|
||||
});
|
||||
|
||||
interface PolicySpecItem {
|
||||
name: keyof typeof policyRegistry.available;
|
||||
params?: Record<string, ParamValue | ParamValue[]>;
|
||||
}
|
||||
|
||||
export interface PolicySpec {
|
||||
policies: PolicySpecItem[];
|
||||
}
|
||||
|
||||
export const DEFAULT_POLICY_SPEC: PolicySpec = {
|
||||
policies: [
|
||||
{ 'name': 'AntiDuplicationPolicy' },
|
||||
{ 'name': 'HellthreadPolicy' },
|
||||
{ 'name': 'ReplyBotPolicy' },
|
||||
{ 'name': 'SizePolicy' },
|
||||
{ 'name': 'HashtagPolicy', 'params': { 'hashtags': ['NSFW', 'explicit', 'violence', 'cp', 'porn'] } },
|
||||
],
|
||||
};
|
||||
|
||||
export const createPolicyEvent = async (conf: DittoConf, policies: PolicySpec) => {
|
||||
return await conf.signer.signEvent({
|
||||
kind: 11984,
|
||||
content: JSON.stringify(policies),
|
||||
created_at: nostrNow(),
|
||||
tags: [],
|
||||
});
|
||||
};
|
||||
50
packages/ditto/utils/policies/mod.ts
Normal file
50
packages/ditto/utils/policies/mod.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import { nostrNow } from '@/utils.ts';
|
||||
import type { DittoConf } from '@ditto/conf';
|
||||
import { PolicyRegistry } from './registry.ts';
|
||||
import { MockRelay } from '@nostrify/nostrify/test';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
|
||||
type ParamValue = string | number | boolean;
|
||||
|
||||
export { PolicyRegistry };
|
||||
|
||||
export const policyRegistry = new PolicyRegistry({
|
||||
antiDuplicationPolicyStore: {
|
||||
get: (key: Deno.KvKey) => Promise.resolve({ key, value: null, versionstamp: null }),
|
||||
set: () => Promise.resolve({ ok: true, versionstamp: '00000000000000000000' }),
|
||||
},
|
||||
store: new MockRelay(),
|
||||
});
|
||||
|
||||
interface PolicySpecItem {
|
||||
name: keyof typeof policyRegistry.available;
|
||||
params?: Record<string, ParamValue | (number | string)[]>;
|
||||
}
|
||||
|
||||
export interface PolicySpec {
|
||||
policies: PolicySpecItem[];
|
||||
}
|
||||
|
||||
export const normalizeNpub = (itm: string) => {
|
||||
if (!itm.startsWith('npub1')) return itm;
|
||||
return nip19.decode(itm as `npub1${string}`).data;
|
||||
};
|
||||
|
||||
export const DEFAULT_POLICY_SPEC: PolicySpec = {
|
||||
policies: [
|
||||
{ 'name': 'AntiDuplicationPolicy' },
|
||||
{ 'name': 'HellthreadPolicy' },
|
||||
{ 'name': 'ReplyBotPolicy' },
|
||||
{ 'name': 'SizePolicy' },
|
||||
{ 'name': 'HashtagPolicy', 'params': { 'hashtags': ['NSFW', 'explicit', 'violence', 'cp', 'porn'] } },
|
||||
],
|
||||
};
|
||||
|
||||
export const createPolicyEvent = async (conf: DittoConf, policies: PolicySpec) => {
|
||||
return await conf.signer.signEvent({
|
||||
kind: 11984,
|
||||
content: JSON.stringify(policies),
|
||||
created_at: nostrNow(),
|
||||
tags: [],
|
||||
});
|
||||
};
|
||||
105
packages/ditto/utils/policies/parameters.test.ts
Normal file
105
packages/ditto/utils/policies/parameters.test.ts
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import { assertEquals } from '@std/assert';
|
||||
import { z } from 'zod';
|
||||
import { zodSchemaToFields } from './parameters.ts';
|
||||
|
||||
Deno.test('zodSchemaToFields - basic types', () => {
|
||||
const schema = z.object({
|
||||
name: z.string(),
|
||||
age: z.number(),
|
||||
});
|
||||
|
||||
const result = zodSchemaToFields(schema);
|
||||
assertEquals(result, {
|
||||
name: { type: 'string' },
|
||||
age: { type: 'number' },
|
||||
});
|
||||
});
|
||||
|
||||
Deno.test('zodSchemaToFields - array types', () => {
|
||||
const schema = z.object({
|
||||
tags: z.array(z.string()),
|
||||
scores: z.array(z.number()),
|
||||
});
|
||||
|
||||
const result = zodSchemaToFields(schema);
|
||||
assertEquals(result, {
|
||||
tags: { type: 'multi_string' },
|
||||
scores: { type: 'multi_number' },
|
||||
});
|
||||
});
|
||||
|
||||
Deno.test('zodSchemaToFields - special-case NIP-01 filters', () => {
|
||||
const schema = z.object({
|
||||
filters: z.array(z.string()),
|
||||
keywords: z.array(z.string()),
|
||||
});
|
||||
|
||||
const result = zodSchemaToFields(schema);
|
||||
assertEquals(result, {
|
||||
filters: { type: 'multi_string' },
|
||||
keywords: { type: 'multi_string' },
|
||||
});
|
||||
});
|
||||
|
||||
Deno.test('zodSchemaToFields - mixed types', () => {
|
||||
const schema = z.object({
|
||||
id: z.string(),
|
||||
values: z.array(z.number()),
|
||||
flags: z.array(z.string()).describe('Test description'),
|
||||
});
|
||||
|
||||
const result = zodSchemaToFields(schema);
|
||||
assertEquals(result, {
|
||||
id: { type: 'string' },
|
||||
values: { type: 'multi_number' },
|
||||
flags: { type: 'multi_string', description: 'Test description' },
|
||||
});
|
||||
});
|
||||
|
||||
Deno.test('zodSchemaToFields - optional fields', () => {
|
||||
const schema = z.object({
|
||||
name: z.string().optional(),
|
||||
age: z.number().optional(),
|
||||
});
|
||||
|
||||
const result = zodSchemaToFields(schema);
|
||||
assertEquals(result, {
|
||||
name: { type: 'string', optional: true },
|
||||
age: { type: 'number', optional: true },
|
||||
});
|
||||
});
|
||||
|
||||
Deno.test('zodSchemaToFields - default values', () => {
|
||||
const schema = z.object({
|
||||
name: z.string().default('John Doe'),
|
||||
age: z.number().default(30),
|
||||
});
|
||||
|
||||
const result = zodSchemaToFields(schema);
|
||||
assertEquals(result, {
|
||||
name: { type: 'string', default: 'John Doe' },
|
||||
age: { type: 'number', default: 30 },
|
||||
});
|
||||
});
|
||||
|
||||
Deno.test('zodSchemaToFields - boolean fields', () => {
|
||||
const schema = z.object({
|
||||
active: z.boolean(),
|
||||
});
|
||||
|
||||
const result = zodSchemaToFields(schema);
|
||||
assertEquals(result, {
|
||||
active: { type: 'boolean' },
|
||||
});
|
||||
});
|
||||
|
||||
Deno.test('zodSchemaToFields - invalid schema', () => {
|
||||
const schema = z.object({
|
||||
invalid: z.any(),
|
||||
});
|
||||
|
||||
const result = zodSchemaToFields(schema);
|
||||
assertEquals(result, {
|
||||
invalid: { type: 'unknown' },
|
||||
});
|
||||
});
|
||||
90
packages/ditto/utils/policies/parameters.ts
Normal file
90
packages/ditto/utils/policies/parameters.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
type FieldType = 'string' | 'multi_string' | 'number' | 'multi_number' | 'boolean' | 'unknown';
|
||||
|
||||
export interface FieldItem {
|
||||
type: FieldType;
|
||||
description?: string;
|
||||
optional?: boolean;
|
||||
default?: any;
|
||||
}
|
||||
|
||||
interface UnwrappedZodType {
|
||||
baseType: z.ZodTypeAny;
|
||||
optional?: boolean;
|
||||
defaultValue?: any;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the base type from wrapped Zod types like ZodOptional and ZodDefault.
|
||||
*/
|
||||
function unwrapZodType(field: z.ZodTypeAny): UnwrappedZodType {
|
||||
let optional = false;
|
||||
let defaultValue: any = undefined;
|
||||
let description: string | undefined = undefined;
|
||||
|
||||
description = field.description;
|
||||
|
||||
while (field instanceof z.ZodOptional || field instanceof z.ZodDefault) {
|
||||
if (field instanceof z.ZodOptional) optional = true;
|
||||
if (field instanceof z.ZodDefault) defaultValue = field._def.defaultValue();
|
||||
if (!description) description = field.description;
|
||||
|
||||
field = field._def.innerType;
|
||||
}
|
||||
|
||||
const result: UnwrappedZodType = { baseType: field };
|
||||
|
||||
if (optional) result.optional = true;
|
||||
if (typeof defaultValue !== 'undefined') {
|
||||
if (
|
||||
typeof defaultValue === 'string' && defaultValue.length > 0 ||
|
||||
(typeof defaultValue === 'number') ||
|
||||
(typeof defaultValue === 'object' && Object.keys(defaultValue).length > 0) ||
|
||||
(Array.isArray(defaultValue) && defaultValue.length > 0)
|
||||
) {
|
||||
result.defaultValue = defaultValue;
|
||||
}
|
||||
}
|
||||
if (description) result.description = description;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a Zod schema into a record of field types. NOT meant to be used
|
||||
* as a general-purpose serializer - this is specifically for generating policy
|
||||
* parameter descriptions! This function also parses internal Zod properties,
|
||||
* but there is precedent for this:
|
||||
* https://github.com/colinhacks/zod/discussions/1953
|
||||
*
|
||||
* With version pinning and the Zod maintainers' knowledge of this kind of
|
||||
* usage, it should be fine for it to be this way.
|
||||
*
|
||||
* Special-cases NIP-01 filters as `multi_string`.
|
||||
*/
|
||||
export function zodSchemaToFields(schema: z.ZodObject<any>): Record<string, FieldItem> {
|
||||
const result: Record<string, FieldItem> = {};
|
||||
|
||||
for (const [key, field] of Object.entries(schema.shape) as [string, z.ZodTypeAny][]) {
|
||||
const { baseType, optional, defaultValue, description } = unwrapZodType(field);
|
||||
result[key] = { type: 'unknown' };
|
||||
if (optional) result[key].optional = optional;
|
||||
if (defaultValue) result[key].default = defaultValue;
|
||||
if (description) result[key].description = description;
|
||||
|
||||
if (key === 'filters') {
|
||||
result[key].type = 'multi_string';
|
||||
} else if (baseType instanceof z.ZodArray) {
|
||||
const elementType = unwrapZodType(baseType._def.type).baseType._def.typeName;
|
||||
if (elementType === 'ZodNumber') result[key].type = 'multi_number';
|
||||
else result[key].type = 'multi_string';
|
||||
} else if (baseType instanceof z.ZodNumber) result[key].type = 'number';
|
||||
else if (baseType instanceof z.ZodBoolean) result[key].type = 'boolean';
|
||||
else if (baseType instanceof z.ZodString) result[key].type = 'string';
|
||||
|
||||
if (baseType.description && !result[key].description) result[key].description = baseType.description;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
218
packages/ditto/utils/policies/registry.ts
Normal file
218
packages/ditto/utils/policies/registry.ts
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
import {
|
||||
AntiDuplicationPolicy,
|
||||
AuthorPolicy,
|
||||
DomainPolicy,
|
||||
FiltersPolicy,
|
||||
HashtagPolicy,
|
||||
HellthreadPolicy,
|
||||
KeywordPolicy,
|
||||
OpenAIPolicy,
|
||||
PowPolicy,
|
||||
PubkeyBanPolicy,
|
||||
RegexPolicy,
|
||||
ReplyBotPolicy,
|
||||
SizePolicy,
|
||||
WhitelistPolicy,
|
||||
WoTPolicy,
|
||||
} from '@nostrify/policies';
|
||||
import { FieldItem, zodSchemaToFields } from './parameters.ts';
|
||||
import { NPolicy, NStore } from '@nostrify/types';
|
||||
import {
|
||||
AntiDuplicationPolicyOpts,
|
||||
AntiDuplicationPolicyOptsSchema,
|
||||
DomainPolicyOptsSchema,
|
||||
FiltersPolicyOptsSchema,
|
||||
HashtagPolicyOptsSchema,
|
||||
HellthreadPolicyOptsSchema,
|
||||
KeywordPolicyOptsSchema,
|
||||
OpenAIPolicyOptsSchema,
|
||||
PowPolicyOptsSchema,
|
||||
PubkeyBanPolicyOptsSchema,
|
||||
RegexPolicyOptsSchema,
|
||||
ReplyBotPolicyOptsSchema,
|
||||
SizePolicyOptsSchema,
|
||||
WhitelistPolicyOptsSchema,
|
||||
WoTPolicyOptsSchema,
|
||||
} from '@/utils/policies/schemas.ts';
|
||||
import { normalizeNpub } from '@/utils/policies/mod.ts';
|
||||
|
||||
export interface PolicyItem {
|
||||
instantiate: (params: Record<string, any>) => NPolicy;
|
||||
name: string;
|
||||
parameters: Record<string, FieldItem>;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface PolicyRegistryOpts {
|
||||
antiDuplicationPolicyStore?: AntiDuplicationPolicyOpts['kv'];
|
||||
store: NStore;
|
||||
}
|
||||
|
||||
export class PolicyRegistry {
|
||||
constructor(private opts: PolicyRegistryOpts) {}
|
||||
|
||||
available: Record<string, PolicyItem> = {
|
||||
'AntiDuplicationPolicy': {
|
||||
instantiate: (params) => {
|
||||
const parsed = AntiDuplicationPolicyOptsSchema.parse(params);
|
||||
if (!this.opts.antiDuplicationPolicyStore) {
|
||||
throw new Error('AntiDuplicationPolicy: tried to instantiate store but no store supplied!');
|
||||
}
|
||||
return new AntiDuplicationPolicy({
|
||||
kv: this.opts.antiDuplicationPolicyStore,
|
||||
...parsed,
|
||||
});
|
||||
},
|
||||
description: 'Prevent messages with the exact same content from being submitted repeatedly.',
|
||||
name: 'Deduplicate messages',
|
||||
parameters: zodSchemaToFields(AntiDuplicationPolicyOptsSchema),
|
||||
},
|
||||
'AuthorPolicy': {
|
||||
instantiate: () => {
|
||||
return new AuthorPolicy(this.opts.store);
|
||||
},
|
||||
description: 'Rejects events by authors without a kind 0 event associated with their pubkey.',
|
||||
name: 'Block events without associated profiles',
|
||||
parameters: {},
|
||||
},
|
||||
'DomainPolicy': {
|
||||
instantiate: (params) => {
|
||||
const parsed = DomainPolicyOptsSchema.parse(params);
|
||||
return new DomainPolicy(this.opts.store, parsed);
|
||||
},
|
||||
description: 'Ban events by pubkeys without a valid NIP-05 domain. Domains can also be whitelisted/blacklisted',
|
||||
name: 'Filter by NIP-05',
|
||||
parameters: zodSchemaToFields(DomainPolicyOptsSchema),
|
||||
},
|
||||
'FiltersPolicy': {
|
||||
instantiate: (params) => {
|
||||
if (!params.filters || !Array.isArray(params.filters)) throw new Error('Invalid params to FiltersPolicy');
|
||||
const filters = params.filters.map((item) => {
|
||||
try {
|
||||
return JSON.parse(item);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}).filter(Boolean);
|
||||
const parsed = FiltersPolicyOptsSchema.parse({ filters });
|
||||
return new FiltersPolicy(parsed.filters);
|
||||
},
|
||||
description: 'Only allow events matching a given Nostr filter',
|
||||
name: 'Filter by Nostr filter',
|
||||
parameters: zodSchemaToFields(FiltersPolicyOptsSchema),
|
||||
},
|
||||
'HashtagPolicy': {
|
||||
instantiate: (params) => {
|
||||
const parsed = HashtagPolicyOptsSchema.parse(params);
|
||||
return new HashtagPolicy(parsed.hashtags);
|
||||
},
|
||||
description: 'Ban events containing the specified hashtags',
|
||||
name: 'Ban hashtags',
|
||||
parameters: zodSchemaToFields(HashtagPolicyOptsSchema),
|
||||
},
|
||||
'HellthreadPolicy': {
|
||||
instantiate: (params) => {
|
||||
const parsed = HellthreadPolicyOptsSchema.parse(params);
|
||||
return new HellthreadPolicy({
|
||||
...parsed,
|
||||
});
|
||||
},
|
||||
description: "Prevent 'hellthreads' - notes that tag hundreds of people to cause a nuisance and server load.",
|
||||
name: 'Limit events with excessive mentions',
|
||||
parameters: zodSchemaToFields(HellthreadPolicyOptsSchema),
|
||||
},
|
||||
'KeywordPolicy': {
|
||||
instantiate: (params) => {
|
||||
const parsed = KeywordPolicyOptsSchema.parse(params);
|
||||
return new KeywordPolicy(parsed.keywords);
|
||||
},
|
||||
description: 'Ban events that contain specified keywords.',
|
||||
name: 'Block certain words',
|
||||
parameters: zodSchemaToFields(KeywordPolicyOptsSchema),
|
||||
},
|
||||
'OpenAIPolicy': {
|
||||
instantiate: (params) => {
|
||||
const parsed = OpenAIPolicyOptsSchema.parse(params);
|
||||
return new OpenAIPolicy({
|
||||
...parsed,
|
||||
});
|
||||
},
|
||||
description: "Use OpenAI moderation integration to block posts that don't meet your community guidelines.",
|
||||
name: 'Use OpenAI moderation',
|
||||
parameters: zodSchemaToFields(OpenAIPolicyOptsSchema),
|
||||
},
|
||||
'PowPolicy': {
|
||||
instantiate: (params) => {
|
||||
const parsed = PowPolicyOptsSchema.parse(params);
|
||||
return new PowPolicy({
|
||||
...parsed,
|
||||
});
|
||||
},
|
||||
description: 'Use proof-of-work to limit events from spammers.',
|
||||
name: 'Require proof-of-work for events',
|
||||
parameters: zodSchemaToFields(PowPolicyOptsSchema),
|
||||
},
|
||||
'PubkeyBanPolicy': {
|
||||
instantiate: (params) => {
|
||||
const parsed = PubkeyBanPolicyOptsSchema.parse(params);
|
||||
return new PubkeyBanPolicy(parsed.pubkeys.map(normalizeNpub));
|
||||
},
|
||||
description: 'Ban events from certain pubkeys',
|
||||
name: 'Ban certain pubkeys',
|
||||
parameters: zodSchemaToFields(PubkeyBanPolicyOptsSchema),
|
||||
},
|
||||
'RegexPolicy': {
|
||||
instantiate: (params) => {
|
||||
const parsed = RegexPolicyOptsSchema.parse(params);
|
||||
return new RegexPolicy(new RegExp(parsed.expr, parsed.flags));
|
||||
},
|
||||
description: 'Ban events that match a certain regular expression.',
|
||||
name: 'Filter by regex',
|
||||
parameters: zodSchemaToFields(RegexPolicyOptsSchema),
|
||||
},
|
||||
'ReplyBotPolicy': {
|
||||
instantiate: (params) => {
|
||||
const parsed = ReplyBotPolicyOptsSchema.parse(params);
|
||||
return new ReplyBotPolicy({
|
||||
store: this.opts.store,
|
||||
...parsed,
|
||||
});
|
||||
},
|
||||
description: 'Block events that reply too quickly to other events.',
|
||||
name: 'Block reply spambots',
|
||||
parameters: zodSchemaToFields(ReplyBotPolicyOptsSchema),
|
||||
},
|
||||
'SizePolicy': {
|
||||
instantiate: (params) => {
|
||||
const parsed = SizePolicyOptsSchema.parse(params);
|
||||
return new SizePolicy({
|
||||
...parsed,
|
||||
});
|
||||
},
|
||||
description: 'Restrict events that are too big in size.',
|
||||
name: 'Block events by size',
|
||||
parameters: zodSchemaToFields(SizePolicyOptsSchema),
|
||||
},
|
||||
'WhitelistPolicy': {
|
||||
instantiate: (params) => {
|
||||
const parsed = WhitelistPolicyOptsSchema.parse(params);
|
||||
return new WhitelistPolicy(parsed.pubkeys.map(normalizeNpub));
|
||||
},
|
||||
description: 'Allow only whitelisted pubkeys to post. All other events are rejected.',
|
||||
name: 'Whitelist people',
|
||||
parameters: zodSchemaToFields(WhitelistPolicyOptsSchema),
|
||||
},
|
||||
'WoTPolicy': {
|
||||
instantiate: (params) => {
|
||||
const parsed = WoTPolicyOptsSchema.parse(params);
|
||||
return new WoTPolicy({
|
||||
store: this.opts.store,
|
||||
...parsed,
|
||||
});
|
||||
},
|
||||
description: 'Use a web-of-trust to only allow users a certain distance from trusted users to publish posts.',
|
||||
name: 'Build a web-of-trust',
|
||||
parameters: zodSchemaToFields(WoTPolicyOptsSchema),
|
||||
},
|
||||
};
|
||||
}
|
||||
150
packages/ditto/utils/policies/schemas.ts
Normal file
150
packages/ditto/utils/policies/schemas.ts
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
import { NostrEvent, NProfilePointer, NStore } from '@nostrify/types';
|
||||
import { z } from 'zod';
|
||||
import { NSchema as n } from '@nostrify/nostrify';
|
||||
|
||||
/** Options for the `WoTPolicy`. */
|
||||
export const WoTPolicyOptsSchema = z.object({
|
||||
pubkeys: z.array(z.string()),
|
||||
depth: z.number().default(2),
|
||||
});
|
||||
|
||||
export interface WoTPolicyOpts extends z.TypeOf<typeof WoTPolicyOptsSchema> {
|
||||
store: NStore;
|
||||
}
|
||||
|
||||
/** Options for `FiltersPolicy`. */
|
||||
export const FiltersPolicyOptsSchema = z.object({
|
||||
filters: z.array(n.filter()),
|
||||
});
|
||||
|
||||
interface FiltersPolicyOpts extends z.TypeOf<typeof FiltersPolicyOptsSchema> { }
|
||||
|
||||
/** Options for `HashtagPolicy`. */
|
||||
export const HashtagPolicyOptsSchema = z.object({
|
||||
hashtags: z.array(z.string()).describe('Banned hashtags (case-insensitive)'),
|
||||
});
|
||||
|
||||
export interface HashtagPolicyOpts extends z.TypeOf<typeof HashtagPolicyOptsSchema> { }
|
||||
|
||||
/** Options for `WhitelistPolicy`. */
|
||||
export const WhitelistPolicyOptsSchema = z.object({
|
||||
pubkeys: z.array(z.string()).describe('Allowed pubkeys'),
|
||||
});
|
||||
|
||||
export interface WhitelistPolicyOpts extends z.TypeOf<typeof WhitelistPolicyOptsSchema> { }
|
||||
|
||||
/** Options for `RegexPolicy`. */
|
||||
export const RegexPolicyOptsSchema = z.object({
|
||||
expr: z.string().describe('Expression'),
|
||||
flags: z.string().optional().describe('Additional RegExp flags. Leave blank if unsure.'),
|
||||
});
|
||||
|
||||
export interface RegexPolicyOpts extends Partial<z.TypeOf<typeof RegexPolicyOptsSchema>> {
|
||||
expr: string;
|
||||
}
|
||||
|
||||
/** Options for `KeywordPolicy`. */
|
||||
export const KeywordPolicyOptsSchema = z.object({
|
||||
keywords: z.array(z.string()).describe('Banned keywords (case-insensitive)'),
|
||||
});
|
||||
|
||||
export interface KeywordPolicyOpts extends z.TypeOf<typeof KeywordPolicyOptsSchema> { }
|
||||
|
||||
/** Options for `DomainPolicy`. */
|
||||
export const DomainPolicyOptsSchema = z.object({
|
||||
blacklist: z.array(z.string()).default([]).describe('Domains to blacklist'),
|
||||
whitelist: z.array(z.string()).optional().describe('Domains to whitelist'),
|
||||
});
|
||||
|
||||
export interface DomainPolicyOpts extends Partial<z.TypeOf<typeof DomainPolicyOptsSchema>> {
|
||||
lookup?(nip05: string, signal?: AbortSignal): Promise<NProfilePointer>;
|
||||
store: NStore;
|
||||
}
|
||||
|
||||
/** Options for `PubkeyBanPolicy`. */
|
||||
export const PubkeyBanPolicyOptsSchema = z.object({
|
||||
pubkeys: z.array(z.string()).describe('Banned pubkeys'),
|
||||
});
|
||||
|
||||
export interface PubkeyBanPolicyOpts extends z.TypeOf<typeof PubkeyBanPolicyOptsSchema> { }
|
||||
|
||||
/** Options for `OpenAIPolicy`. */
|
||||
export const OpenAIPolicyOptsSchema = z.object({
|
||||
endpoint: z.string().default('https://api.openai.com/v1/moderations').describe('OpenAI API endpoint'),
|
||||
kinds: z.array(z.number()).default([1, 30023]).describe('Event kinds to filter through the policy'),
|
||||
apiKey: z.string().describe('OpenAI API key'),
|
||||
});
|
||||
|
||||
interface OpenAIModerationResult {
|
||||
id: string;
|
||||
model: string;
|
||||
results: {
|
||||
categories: {
|
||||
'hate': boolean;
|
||||
'hate/threatening': boolean;
|
||||
'self-harm': boolean;
|
||||
'sexual': boolean;
|
||||
'sexual/minors': boolean;
|
||||
'violence': boolean;
|
||||
'violence/graphic': boolean;
|
||||
};
|
||||
category_scores: {
|
||||
'hate': number;
|
||||
'hate/threatening': number;
|
||||
'self-harm': number;
|
||||
'sexual': number;
|
||||
'sexual/minors': number;
|
||||
'violence': number;
|
||||
'violence/graphic': number;
|
||||
};
|
||||
flagged: boolean;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface OpenAIPolicyOpts extends Partial<z.TypeOf<typeof OpenAIPolicyOptsSchema>> {
|
||||
apiKey: string;
|
||||
handler?(event: NostrEvent, result: OpenAIModerationResult): boolean;
|
||||
fetch?: typeof fetch;
|
||||
}
|
||||
|
||||
/** Options for `ReplyBotPolicy`. */
|
||||
export const ReplyBotPolicyOptsSchema = z.object({
|
||||
threshold: z.number().default(1).describe('Minimum time in seconds between two posts.'),
|
||||
kinds: z.array(z.number()).default([1]).describe('Event kinds to filter'),
|
||||
});
|
||||
|
||||
export interface ReplyBotPolicyOpts extends Partial<z.TypeOf<typeof ReplyBotPolicyOptsSchema>> {
|
||||
store: NStore;
|
||||
}
|
||||
|
||||
/** Options for `SizePolicy`. */
|
||||
export const SizePolicyOptsSchema = z.object({
|
||||
maxBytes: z.number().default(8 * 1024).describe('Max allowed message size in bytes'),
|
||||
});
|
||||
|
||||
export interface SizePolicyOpts extends Partial<z.TypeOf<typeof SizePolicyOptsSchema>> { }
|
||||
|
||||
/** Options for `AntiDuplicationPolicy`. */
|
||||
export const AntiDuplicationPolicyOptsSchema = z.object({
|
||||
expireIn: z.number().default(60000).describe('How long should we wait before an identical message can be reposted?'),
|
||||
minLength: z.number().default(50).describe('Minimum length for filtered messages'),
|
||||
});
|
||||
|
||||
export interface AntiDuplicationPolicyOpts extends Partial<z.TypeOf<typeof AntiDuplicationPolicyOptsSchema>> {
|
||||
kv: Pick<Deno.Kv, 'get' | 'set'>;
|
||||
deobfuscate?(event: NostrEvent): string;
|
||||
}
|
||||
|
||||
/** Options for `HellthreadPolicy`. */
|
||||
export const HellthreadPolicyOptsSchema = z.object({
|
||||
limit: z.number().default(100).describe('Maximum number of mentions to allow per post'),
|
||||
});
|
||||
|
||||
export interface HellthreadPolicyOpts extends Partial<z.TypeOf<typeof HellthreadPolicyOptsSchema>> { }
|
||||
|
||||
/** Options for `PowPolicy`. */
|
||||
export const PowPolicyOptsSchema = z.object({
|
||||
difficulty: z.number().default(1).describe('Number of bits of proof-of-work to require'),
|
||||
});
|
||||
|
||||
export interface PowPolicyOpts extends Partial<z.TypeOf<typeof PowPolicyOptsSchema>> { }
|
||||
|
|
@ -2,12 +2,12 @@ import { DittoConf } from '@ditto/conf';
|
|||
import { DittoPolyPg } from '@ditto/db';
|
||||
import '@soapbox/safe-fetch/load';
|
||||
import { NostrEvent, NostrRelayOK, NPolicy } from '@nostrify/nostrify';
|
||||
import { PipePolicy, PolicyRegistry, ReadOnlyPolicy } from '@nostrify/policies';
|
||||
import { PipePolicy, ReadOnlyPolicy } from '@nostrify/policies';
|
||||
import * as Comlink from 'comlink';
|
||||
|
||||
import { ReadOnlySigner } from '@/signers/ReadOnlySigner.ts';
|
||||
import { DittoPgStore } from '@/storages/DittoPgStore.ts';
|
||||
import { DEFAULT_POLICY_SPEC, PolicySpec } from '@/utils/policies.ts';
|
||||
import { DEFAULT_POLICY_SPEC, PolicyRegistry, PolicySpec } from '@/utils/policies/mod.ts';
|
||||
import { logi } from '@soapbox/logi';
|
||||
|
||||
// @ts-ignore Don't try to access the env from this worker.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue