diff --git a/.gitignore b/.gitignore index 25c8fdb..eb7cd40 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules -package-lock.json \ No newline at end of file +package-lock.json +strfry-db diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..3af7f5d --- /dev/null +++ b/compose.yml @@ -0,0 +1,13 @@ +version: "3.8" + +services: + strfry: + image: strfry-custom + container_name: strfry-custom + ports: + - 7777:7777 + volumes: + - ./strfry-db:/app/strfry-db + - ./strfry.conf:/etc/strfry.conf + restart: always + stop_grace_period: 2m diff --git a/package.json b/package.json index ced9bbc..9e06122 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "1.0.0", "description": "", "main": "whitelist.js", + "type": "module", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, diff --git a/strfry.conf b/strfry.conf index fa786f8..1295725 100644 --- a/strfry.conf +++ b/strfry.conf @@ -130,9 +130,9 @@ writePolicy { negentropy { # Support negentropy protocol messages - enabled = true + enabled = false # Maximum records that sync will process before returning an error maxSyncEvents = 1000000 } -} \ No newline at end of file +} diff --git a/whitelist.js b/whitelist.js index 30c1a94..38e8ea3 100644 --- a/whitelist.js +++ b/whitelist.js @@ -1,87 +1,119 @@ #!/usr/bin/env node -const axios = require("axios"); -const nostrTools = require("nostr-tools"); +import axios from "axios"; +import * as nostrTools from "nostr-tools"; +import readline from "readline"; -const GRAPHQL_URL = "https://api.flashapp.me/graphql"; // Replace with your actual GraphQL endpoint +const GRAPHQL_URL = "https://api.flashapp.me/graphql"; -const rl = require("readline").createInterface({ +const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false, }); -const checkWhitelist = async (npub) => { - let encodedNpub = nostrTools.nip19.npubEncode(npub); - const query = ` - query Query($input: IsFlashNpubInput!) { - isFlashNpub(input: $input) { - isFlashNpub +const checkWhitelist = async (hexPubkey) => { + try { + const npub = nostrTools.nip19.npubEncode(hexPubkey); + + const query = ` + query Query($input: IsFlashNpubInput!) { + isFlashNpub(input: $input) { + isFlashNpub + } } - } - `; + `; - const variables = { - input: { npub: encodedNpub }, - }; + const variables = { + input: { npub }, + }; - console.error( - "Making API request with variables:", - JSON.stringify(variables) - ); // Log request to stderr + console.error("Checking whitelist:", JSON.stringify(variables)); - try { const response = await axios.post( GRAPHQL_URL, - { - query, - variables, - }, - { - headers: { "Content-Type": "application/json" }, - } + { query, variables }, + { headers: { "Content-Type": "application/json" } }, ); - console.error("API response:", response.data); // Log response to stderr - return response.data.data.isFlashNpub.isFlashNpub; - } catch (error) { - console.error("Error fetching whitelist status:", error.message); - console.error("Request variables were:", JSON.stringify(variables)); + + return Boolean(response?.data?.data?.isFlashNpub?.isFlashNpub); + } catch (err) { + console.error("Whitelist check failed:", err.message); return false; } }; rl.on("line", async (line) => { let req; + const res = {}; try { req = JSON.parse(line); - } catch (error) { - console.error("Invalid JSON format"); + } catch { + console.log(JSON.stringify({ action: "reject", msg: "invalid JSON" })); return; } + if (!req.event?.id) { + console.log(JSON.stringify({ action: "reject", msg: "missing event id" })); + return; + } + + res.id = req.event.id; + if (req.type !== "new") { - console.error("unexpected request type"); + res.action = "reject"; + res.msg = "unexpected request type"; + console.log(JSON.stringify(res)); return; } - const npub = req.event.tags.filter((t) => t[0] === "p")[0]?.[1]; - console.error("npub is", npub, "Request event is", req.event); - if (!npub) { - console.error("No referenced npub"); + const { kind, pubkey, tags = [] } = req.event; + + console.error("Processing event:", { id: res.id, kind, pubkey }); + + // kind 0, 1 & 3 → author must be whitelisted + if (kind === 0 || kind === 1 || kind === 3) { + if (!pubkey) { + res.action = "reject"; + res.msg = "missing pubkey"; + } else if (await checkWhitelist(pubkey)) { + res.action = "accept"; + } else { + res.action = "reject"; + res.msg = "blocked: author not on whitelist"; + } + + console.log(JSON.stringify(res)); return; } - // Check if the pubkey is on the whitelist via GraphQL query - const isWhitelisted = await checkWhitelist(npub); + // kind 1059 → any p-tag must be whitelisted + if (kind === 1059) { + const pTags = tags.filter((t) => t[0] === "p" && t[1]).map((t) => t[1]); + + if (!pTags.length) { + res.action = "reject"; + res.msg = "missing p tag"; + console.log(JSON.stringify(res)); + return; + } + + for (const hex of pTags) { + if (await checkWhitelist(hex)) { + res.action = "accept"; + console.log(JSON.stringify(res)); + return; + } + } - let res = { id: req.event.id }; // echo event's id - if (isWhitelisted) { - res.action = "accept"; - } else { res.action = "reject"; - res.msg = "blocked: not on white-list"; + res.msg = "blocked: no recipient on whitelist"; + console.log(JSON.stringify(res)); + return; } + res.action = "reject"; + res.msg = `unsupported kind ${kind}`; console.log(JSON.stringify(res)); });