-- Leo's gemini proxy
-- Connecting to git.thebackupbox.net:1965...
-- Connected
-- Sending request
-- Meta line: 20 text/gemini
repo: nostr action: commit revision: path_from: revision_from: dcd949c36658df03c6ac2861664af7d052e8e87a: path_to: revision_to:
commit dcd949c36658df03c6ac2861664af7d052e8e87a Author: epoch <epoch@thebackupbox.net> Date: Wed Apr 26 02:42:49 2023 +0000 went too long without putting this in a repo diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..2ed413eb3cf75be48f5c2ff4619de17fdd691702 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +PREFIX:=/usr/local + +install: + install -Dt $(PREFIX)/libexec/ nostr-relay.sh + install -Dt $(PREFIX)/libexec/ nostr-relay-req.sh diff --git a/client.html b/client.html new file mode 100755 index 0000000000000000000000000000000000000000..ab81a722c19b4d72d649f3c7d78ff228f3d72947 --- /dev/null +++ b/client.html @@ -0,0 +1,183 @@ +<html> + <head> + <title></title> + </head> +<style> +body { + font-family: sans-serif; +} +.post { + border-style: solid; + border-width: 3px; +} +</style> +<script> + +event_kind = Array(); +event_kind[0] = "Metadata"; +event_kind[1] = "Short Text Note"; +event_kind[2] = "Recommend Relay"; +event_kind[3] = "Contacts"; +event_kind[4] = "Encrypted Direct Messages"; +event_kind[5] = "Event Deletion"; +event_kind[6] = "Resposts"; +event_kind[7] = "Reaction"; +event_kind[8] = "Badge Award"; +event_kind[40] = "Channel Creation"; +event_kind[41] = "Channel Metadata"; +event_kind[42] = "Channel Message"; +event_kind[43] = "Channel Hide Message"; +event_kind[44] = "Channel Mute User"; +event_kind[1063] = "File Metadata"; +event_kind[1984] = "Reporting"; +event_kind[9734] = "Zap Request"; +event_kind[9735] = "Zap"; +event_kind[10000] = "Mute List"; +event_kind[10001] = "Pin List"; +event_kind[10002] = "Relay List Metadata"; +event_kind[22242] = "Client Authentication"; +event_kind[24133] = "Nostr Connect"; +event_kind[30000] = "Categorized People List"; +event_kind[30001] = "Categorized Bookmark List"; +event_kind[30008] = "Profile Badges"; +event_kind[30009] = "Badge Definition"; +event_kind[30017] = "Create or update a stall"; +event_kind[30018] = "Create or update a product"; +event_kind[30023] = "Long-form Content"; +event_kind[30078] = "Application-specific Data"; + +var i=0; +var public_key=""; +var relays; + +var open_subs=[]; + +function set_public_key(x) { + public_key=x; + document.getElementById("pubkey").value=x; +} + +function send_req(ws) { + uuid=crypto.randomUUID(); + msg=["REQ",crypto.randomUUID(),{"kinds":[4],"#p":[public_key],"limit":1000}]; + ws.send(JSON.stringify(msg)); + open_subs.push([msg,ws]); +} + +function set_plaintext(node, str) { + node.textContent = str; +} + +function handle_message(x) { + msg=JSON.parse(x.data); + if(msg[0] != "EVENT") { + console.log(msg); + return; + } + feed=document.getElementById("feed"); + post=document.createElement("div"); + post.setAttribute("class","post"); + post.appendChild(document.createTextNode("msg type: " + msg[0])); + post.appendChild(document.createElement("br")); + post.appendChild(document.createTextNode("from " + x.originalTarget.url)); + post.appendChild(document.createElement("br")); + post.created_at = msg[2].created_at; + post.appendChild(document.createTextNode("created at:" + msg[2].created_at)); + post.appendChild(document.createElement("br")); + post.appendChild(document.createTextNode("sub id:" + msg[1])); + post.appendChild(document.createElement("br")); + post.appendChild(document.createTextNode("event kind: " + event_kind[msg[2].kind] + "(" + msg[2].kind + ")")); + post.appendChild(document.createElement("br")); + author = msg[2].pubkey; + post.appendChild(document.createTextNode("author: " + author)); + post.appendChild(document.createElement("br")); + if(msg[2].kind == 0) { // metadata + metadata=JSON.parse(msg[2].content); + picture=document.createElement("img"); + picture.setAttribute("src",metadata.picture); + post.appendChild(picture); + post.appendChild(document.createTextNode("metadata: " + msg[2].content)); + post.appendChild(document.createElement("br")); + } + if(msg[2].kind == 1) { // short text note + post.appendChild(document.createTextNode("event content: " + msg[2].content)); + post.appendChild(document.createElement("br")); + } + if(msg[2].kind == 4) { // encrypted direct message + post.appendChild(document.createTextNode(x.data)); + mine=0; + for(i=0;i<msg[2].tags.length && !mine;i++) { + if(msg[2].tags[i][0] == "p") { + for(j=1;j<msg[2].tags[i].length && !mine;j++) { + if(msg[2].tags[i][j] == public_key) { + mine=1; + } + } + } + } + if(mine) { + text_node = document.createTextNode("decrypting..."); + post.appendChild(text_node); + window.nostr.nip04.decrypt(msg[2].pubkey,msg[2].content).then(x => { set_plaintext(text_node, x); }) + } else { + post.appendChild(document.createTextNode("not for you")); + } + } + if(msg[2].kind == 6) { // reposts + post.appendChild(document.createTextNode("repost")); + post.appendChild(document.createElement("br")); + } + if(msg[2].kind == 7) { // reaction + post.appendChild(document.createTextNode("reaction")); + post.appendChild(document.createElement("br")); + } + for(i=0;i<feed.children.length;i++) { //loop over all children, and insert before older + if(post.created_at > feed.children[i].created_at) { + feed.insertBefore(post,feed.children[i]); + return; + } + } + feed.appendChild(post,feed); +} + +function set_relays(x) { + relays = x; //relays is a global + urls = Object.keys(relays); + urls.forEach(function(url) { + var ws = new WebSocket(url); + ws.onmessage = function(event) { + handle_message(event); + }; + ws.error = function(error) { + console.log('websocket error: ' + error); + }; + ws.onopen = function(ws) { send_req(this); }; + ws.onclose = function(ws) { console.log("closing " + url); }; + }); +} + +function nostr_onload() { + window.nostr.getPublicKey().then(x => { set_public_key(x); }); + window.nostr.getRelays().then(x => { set_relays(x); }); +} + +function check_for_nostr() { + if(!window.nostr) { + if(i > 10) { // sure + alert("you do not appear to have a nostr plugin"); + } + setTimeout(check_for_nostr,200); + i++; + return; + } + nostr_onload(); +} + +onload = check_for_nostr; +</script> + <body> + your public key: <input id="pubkey" name="pubkey" value="loading..." /> + <div id="feed"> + </div> + </body> +</html> diff --git a/nip-05-test.cgi b/nip-05-test.cgi new file mode 100755 index 0000000000000000000000000000000000000000..8dbd725c7bd907642a83c2ce275bf15f52ef0032 --- /dev/null +++ b/nip-05-test.cgi @@ -0,0 +1,90 @@ +#!/usr/bin/env bash + +acct="$(uriunescape "$(query_param acct)")" +user="$(cut -d@ -f1 <<< "$acct")" +host="$(cut -d@ -f2 <<< "$acct")" +url="https://${host}/.well-known/nostr.json?user=${user}" + +if [ "$1" = "stage2" ];then +# env + printf 'Server: %s<br/>' "$HTTP_SERVER" + if [ ! "$HTTP_ACCESS_CONTROL_ALLOW_ORIGIN" ];then + printf '<span class="warn">WARNING</span>: missing Access-Control-Allow-Origin header<br/>\n' + else + printf '<span class="good">GOOD</span>: Access-Control-Allow-Origin header present!<br/>' + fi + if [ "$HTTP_ACCESS_CONTROL_ALLOW_ORIGIN" != '*' ];then + printf '<details><summary><span class="warn">WARNING</span>: Access-Control-Allow-Origin header has wrong value. it is "%s" and should be "*"</summary>\n' "$HTTP_ACCESS_CONTROL_ALLOW_ORIGIN" + printf 'for NGINX:<br/>' + printf '<pre>' + printf 'location /.well-known/nostr.json {\n' + printf ' add_header Access-Control-Allow-Origin "*";\n' + printf '}\n' + printf '</pre>\n' + printf 'for Apache2:<br/>' + printf '<pre>' + printf 'a2enmod headers\n' + printf 'Header set Access-Control-Allow-Origin "*"\n' + printf '</pre>\n' + printf '</details>' + else + printf '<span class="good">GOOD</span>: Access-Control-Allow-Origin header is set right.<br/>\n' + fi + if [ "$HTTP_CONTENT_TYPE" != "application/json" ];then + printf '<span class="warn">WARNING</span>: content-type returned is not application/json but %s<br/>\n' "$HTTP_CONTENT_TYPE" + else + printf '<span class="good">GOOD</span>: content-type returns is application/json<br/>\n' + fi + RESP_DATA="$(cat)" + printf '<pre>' + #env + if jq -r . <<< "$RESP_DATA";then + printf '</pre>\n' + printf '<span class="good">GOOD</span>: properly formatted JSON.<br/>\n' + else + printf '</pre>\n' + printf '<span class="err">ERROR</span>: JSON failed to parse. see jq error message.<br/>\n' + fi + pubkey="$(jq -r '.names.'"$user"'//""' <<< "$RESP_DATA")" + if [ "${pubkey}" ];then + printf '<span class="good">GOOD</span>: public key found for user %s! %s<br/>' "$user" "$pubkey" + else + printf '<span class="warn">WARNING</span>: public key not found for user %s<br/>' "$user" + fi + npub="$(bech32 -e -h npub "$pubkey")" + printf '<a href="nostr:%s">nostr:%s</a>' "$npub" "$npub" + exit 0 +fi + +printf 'Content-Type: text/html\r\n\r\n' + +printf '<html><head>\n' +printf '<title>nip-05 tester</title>\n' +printf '<style>\n' +cat <<EOF +body { + background-color: black; + color: white; +} +.warn { + color: yellow; +} +.err { + color: red; +} +.good { + color: lime; +} +EOF +printf '</style>\n' +printf '</head><body>' + +if [ "$acct" ];then + printf 'spec: <a href="https://github.com/nostr-protocol/nips/blob/master/05.md">NIP-05</a><br/>' + printf 'user: %s<br/>' "$user" + printf 'host: %s<br/>' "$host" + printf 'url: <a href="%s">%s</a><br/>' "$url" "$url" + curl -A "thebackupbox.net nip-05 tester" -sig "$url" | /usr/local/libexec/read_headers $0 stage2 +fi + +printf '</body></html>' diff --git a/nostr-relay-req.sh b/nostr-relay-req.sh new file mode 100755 index 0000000000000000000000000000000000000000..42a898cbaaf4fca5af10f4d6bf542859e1caf210 --- /dev/null +++ b/nostr-relay-req.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +db=/var/db/nostr/relay + +REMOTE_ADDR="$1" +id="$2" +shift +shift +# filters are now "$@" + +jq --unbuffered -nc 'inputs | [.[0],"'$id'",.[1]]' < "${db}" + +printf '["EOSE", "%s"]\n' "$id" + +### this won't die naturally. we need to have parent process kill it when it dies. + +nonblocktail <(tail -n 0 -f "${db}") | jq --unbuffered -nc 'inputs | [.[0],"'$id'",.[1]]' diff --git a/nostr-relay.sh b/nostr-relay.sh new file mode 100755 index 0000000000000000000000000000000000000000..28335e94613d471764e293e8632093073a676cd5 --- /dev/null +++ b/nostr-relay.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +db=/var/db/nostr/relay + +printf '["NOTICE","hello, %s."]\n' "$REMOTE_ADDR" +if [ "$REMOTE_ADDR" != "21.41.41.4" ];then + printf '["NOTICE","PLEASE DO NOT USE THIS SERVER. STILL BEING WORKED ON."]\n' + exit 0 +fi + +#i=0 +#while true;do +# printf '["NOTICE","%s"]\n' "$(perl -e "print 'A'x$i")" +# i=$[$i+1] +# sleep .2 +#done + +### w00t. thank you https://stackoverflow.com/questions/41599314/ignore-unparseable-json-with-jq +jq -Rnc 'inputs | fromjson?' | while read -r msg;do + logger -p crit "nostr-relay $$ msg: $msg" + case "$(jq -r '.[0]//""' <<< "$msg")" in + EVENT) + if [ "$REMOTE_ADDR" != "21.41.41.4" ];then + printf '["NOTICE","DO NOT USE THIS SERVER. STILL BEING WORKED ON."]\n' + continue + fi + printf '%s\n' "$msg" >> "${db}" + printf '["NOTICE","got an EVENT from you. :)"]\n' + ### TODO: validate event somehow + ### store this event somewhere + ;; + REQ) + ### TODO: make the subid contain + subid="$(jq -r '.[1]' <<< "$msg")" + printf '["NOTICE","got a REQ from you. :)"]\n' + #filter="$(jq -r '.[2]' <<< "$msg")" ## only supporting one filter atm + ### I figure we need to fork off a background process to keep a watch on incoming events from everywhere + ### not sure how well we can CLOSE those though... kill %n based on job id? + stdbuf -o0 /usr/local/libexec/nostr-relay-req.sh "$REMOTE_ADDR" "${subid}" "${filter}" & 2>/dev/null + ### oooooh. + ;; + CLOSE) + subid="$(jq -r '.[1]' <<< "$msg")" + printf '["NOTICE","closing sub id: %s"]\n' "$subid" + ### somehow kill the right process or job id + kill "%/usr/local/libexec/nostr-replay-req.sh ${REMOTE_ADDR} ${subid}" + ;; + DEBUG) + cmd="$(jq -r '.[1]' <<< "$msg")" + printf '["NOTICE","%s"]\n' "$($cmd)" + ;; + *) + logger -p crit "$$ unexpected message: $msg" + printf '["NOTICE","unexpected message type received: '%s' msg: '%s'"]\n' "$type" "$msg" + #exit 0 + ;; + esac + logger -p crit "$$ got passed the case" +done
-----END OF PAGE-----
-- Response ended
-- Page fetched on Sun Jun 2 14:50:07 2024