diff --git a/functions/_middleware.ts b/functions/_middleware.ts index e9450135..70d1c535 100644 --- a/functions/_middleware.ts +++ b/functions/_middleware.ts @@ -1,29 +1,7 @@ interface Env {} -import { bech32 } from "./bech32"; -import { fromHex } from "./hex"; - -interface NostrJson { - names: Record; -} - const HOST = "snort.social"; -async function fetchNostrAddress(name: string, domain: string) { - try { - const res = await fetch(`https://${domain}/.well-known/nostr.json?name=${encodeURIComponent(name)}`, { - signal: AbortSignal.timeout(1000), - }); - const data: NostrJson = await res.json(); - const match = Object.keys(data.names).find(n => { - return n.toLowerCase() === name.toLowerCase(); - }); - return match ? data.names[match] : undefined; - } catch { - // ignored - } -} - export const onRequest: PagesFunction = async context => { const u = new URL(context.request.url); @@ -38,12 +16,7 @@ export const onRequest: PagesFunction = async context => { try { let id = u.pathname.split("/").at(-1); if (!isEntityPath && nostrAddress) { - const pubkey = await fetchNostrAddress(id, HOST); - if (pubkey) { - id = bech32.encode("npub", bech32.toWords(fromHex(pubkey))); - } else { - return next; - } + id = `${id}@${HOST}`; } const fetchApi = `http://nostr.api.v0l.io/api/v1/opengraph/${id}?canonical=${encodeURIComponent( `https://${HOST}/%s`, diff --git a/functions/bech32.ts b/functions/bech32.ts deleted file mode 100644 index 993bf5db..00000000 --- a/functions/bech32.ts +++ /dev/null @@ -1,198 +0,0 @@ -"use strict"; -const ALPHABET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; - -const ALPHABET_MAP: { [key: string]: number } = {}; -for (let z = 0; z < ALPHABET.length; z++) { - const x = ALPHABET.charAt(z); - ALPHABET_MAP[x] = z; -} - -function polymodStep(pre: number): number { - const b = pre >> 25; - return ( - ((pre & 0x1ffffff) << 5) ^ - (-((b >> 0) & 1) & 0x3b6a57b2) ^ - (-((b >> 1) & 1) & 0x26508e6d) ^ - (-((b >> 2) & 1) & 0x1ea119fa) ^ - (-((b >> 3) & 1) & 0x3d4233dd) ^ - (-((b >> 4) & 1) & 0x2a1462b3) - ); -} - -function prefixChk(prefix: string): number | string { - let chk = 1; - for (let i = 0; i < prefix.length; ++i) { - const c = prefix.charCodeAt(i); - if (c < 33 || c > 126) return "Invalid prefix (" + prefix + ")"; - - chk = polymodStep(chk) ^ (c >> 5); - } - chk = polymodStep(chk); - - for (let i = 0; i < prefix.length; ++i) { - const v = prefix.charCodeAt(i); - chk = polymodStep(chk) ^ (v & 0x1f); - } - return chk; -} - -function convert(data: ArrayLike, inBits: number, outBits: number, pad: true): number[]; -function convert(data: ArrayLike, inBits: number, outBits: number, pad: false): number[] | string; -function convert(data: ArrayLike, inBits: number, outBits: number, pad: boolean): number[] | string { - let value = 0; - let bits = 0; - const maxV = (1 << outBits) - 1; - - const result: number[] = []; - for (let i = 0; i < data.length; ++i) { - value = (value << inBits) | data[i]; - bits += inBits; - - while (bits >= outBits) { - bits -= outBits; - result.push((value >> bits) & maxV); - } - } - - if (pad) { - if (bits > 0) { - result.push((value << (outBits - bits)) & maxV); - } - } else { - if (bits >= inBits) return "Excess padding"; - if ((value << (outBits - bits)) & maxV) return "Non-zero padding"; - } - - return result; -} - -function toWords(bytes: ArrayLike): number[] { - return convert(bytes, 8, 5, true); -} - -function fromWordsUnsafe(words: ArrayLike): number[] | undefined { - const res = convert(words, 5, 8, false); - if (Array.isArray(res)) return res; -} - -function fromWords(words: ArrayLike): number[] { - const res = convert(words, 5, 8, false); - if (Array.isArray(res)) return res; - - throw new Error(res); -} - -function getLibraryFromEncoding(encoding: "bech32" | "bech32m"): BechLib { - let ENCODING_CONST: number; - if (encoding === "bech32") { - ENCODING_CONST = 1; - } else { - ENCODING_CONST = 0x2bc830a3; - } - - function encode(prefix: string, words: ArrayLike, LIMIT?: number): string { - LIMIT = LIMIT || 90; - if (prefix.length + 7 + words.length > LIMIT) throw new TypeError("Exceeds length limit"); - - prefix = prefix.toLowerCase(); - - // determine chk mod - let chk = prefixChk(prefix); - if (typeof chk === "string") throw new Error(chk); - - let result = prefix + "1"; - for (let i = 0; i < words.length; ++i) { - const x = words[i]; - if (x >> 5 !== 0) throw new Error("Non 5-bit word"); - - chk = polymodStep(chk) ^ x; - result += ALPHABET.charAt(x); - } - - for (let i = 0; i < 6; ++i) { - chk = polymodStep(chk); - } - chk ^= ENCODING_CONST; - - for (let i = 0; i < 6; ++i) { - const v = (chk >> ((5 - i) * 5)) & 0x1f; - result += ALPHABET.charAt(v); - } - - return result; - } - - function __decode(str: string, LIMIT?: number): Decoded | string { - LIMIT = LIMIT || 90; - if (str.length < 8) return str + " too short"; - if (str.length > LIMIT) return "Exceeds length limit"; - - // don't allow mixed case - const lowered = str.toLowerCase(); - const uppered = str.toUpperCase(); - if (str !== lowered && str !== uppered) return "Mixed-case string " + str; - str = lowered; - - const split = str.lastIndexOf("1"); - if (split === -1) return "No separator character for " + str; - if (split === 0) return "Missing prefix for " + str; - - const prefix = str.slice(0, split); - const wordChars = str.slice(split + 1); - if (wordChars.length < 6) return "Data too short"; - - let chk = prefixChk(prefix); - if (typeof chk === "string") return chk; - - const words = []; - for (let i = 0; i < wordChars.length; ++i) { - const c = wordChars.charAt(i); - const v = ALPHABET_MAP[c]; - if (v === undefined) return "Unknown character " + c; - chk = polymodStep(chk) ^ v; - - // not in the checksum? - if (i + 6 >= wordChars.length) continue; - words.push(v); - } - - if (chk !== ENCODING_CONST) return "Invalid checksum for " + str; - return { prefix, words }; - } - - function decodeUnsafe(str: string, LIMIT?: number): Decoded | undefined { - const res = __decode(str, LIMIT); - if (typeof res === "object") return res; - } - - function decode(str: string, LIMIT?: number): Decoded { - const res = __decode(str, LIMIT); - if (typeof res === "object") return res; - - throw new Error(res); - } - - return { - decodeUnsafe, - decode, - encode, - toWords, - fromWordsUnsafe, - fromWords, - }; -} - -export const bech32 = getLibraryFromEncoding("bech32"); -export const bech32m = getLibraryFromEncoding("bech32m"); -export interface Decoded { - prefix: string; - words: number[]; -} -export interface BechLib { - decodeUnsafe: (str: string, LIMIT?: number | undefined) => Decoded | undefined; - decode: (str: string, LIMIT?: number | undefined) => Decoded; - encode: (prefix: string, words: ArrayLike, LIMIT?: number | undefined) => string; - toWords: typeof toWords; - fromWordsUnsafe: typeof fromWordsUnsafe; - fromWords: typeof fromWords; -} diff --git a/functions/hex.ts b/functions/hex.ts deleted file mode 100644 index 4b19bab1..00000000 --- a/functions/hex.ts +++ /dev/null @@ -1,30 +0,0 @@ -const SHORT_TO_HEX: { [key: number]: string } = {}; -const HEX_TO_SHORT: Record = {}; - -for (let i = 0; i < 256; i++) { - let encodedByte = i.toString(16).toLowerCase(); - if (encodedByte.length === 1) { - encodedByte = `0${encodedByte}`; - } - - SHORT_TO_HEX[i] = encodedByte; - HEX_TO_SHORT[encodedByte] = i; -} - -export function fromHex(encoded: string): Uint8Array { - if (encoded.length % 2 !== 0) { - throw new Error("Hex encoded strings must have an even number length"); - } - - const out = new Uint8Array(encoded.length / 2); - for (let i = 0; i < encoded.length; i += 2) { - const encodedByte = encoded.slice(i, i + 2).toLowerCase(); - if (encodedByte in HEX_TO_SHORT) { - out[i / 2] = HEX_TO_SHORT[encodedByte]; - } else { - throw new Error(`Cannot decode unrecognized sequence ${encodedByte} as hexadecimal`); - } - } - - return out; -}