initial commit
This commit is contained in:
commit
7ce36dfb37
8 changed files with 298 additions and 0 deletions
81
src/main.ts
Normal file
81
src/main.ts
Normal file
|
@ -0,0 +1,81 @@
|
|||
import type { ServerWebSocket } from "bun";
|
||||
|
||||
interface Event {
|
||||
kind: number;
|
||||
tags: string[][];
|
||||
content: string;
|
||||
created_at: number;
|
||||
pubkey: string;
|
||||
id: string;
|
||||
sig: string;
|
||||
}
|
||||
|
||||
type Nip42ProxySocketData = {
|
||||
authenticated: boolean;
|
||||
authToken: string;
|
||||
remoteWs: WebSocket;
|
||||
};
|
||||
|
||||
async function validateAuthEvent(event: Event, challenge: string): boolean {
|
||||
if (event.kind !== 22242) return false;
|
||||
const last30Seconds = Math.floor(Date.now() / 1000) - 30;
|
||||
if (event.created_at < last30Seconds) return false;
|
||||
const challengeTag = event.tags.find(tag => tag[0] === 'challange')?.[1];
|
||||
if (challengeTag !== challenge) return false;
|
||||
const file = Bun.file("./allowed-pubkeys.json");
|
||||
if (!file.exists()) return true;
|
||||
const allowedPubkeys = JSON.parse(await file.text());
|
||||
if (!allowedPubkeys.includes(event.pubkey)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
const sendMessage = (ws: ServerWebSocket<Nip42ProxySocketData>, message: any[]) => ws.send(JSON.stringify(message), true);
|
||||
const sendAuth = (ws: ServerWebSocket<Nip42ProxySocketData>) => sendMessage(ws, ["AUTH", ws.data.authToken, "This is an authenticated relay."]);
|
||||
|
||||
export function main(mainRelayUrl: string) {
|
||||
const server = Bun.serve<Nip42ProxySocketData, {}>({
|
||||
fetch(req, server) {
|
||||
const upgrade = server.upgrade(req, {
|
||||
data: {
|
||||
authenticated: false,
|
||||
authToken: Bun.randomUUIDv7()
|
||||
}
|
||||
});
|
||||
if (!upgrade) console.log(`http request`)
|
||||
return new Response("this is a proxy. Use http to access it");
|
||||
},
|
||||
websocket: {
|
||||
async message(ws, msg) {
|
||||
const [command, ...data] = JSON.parse(msg);
|
||||
if (!ws.data.authenticated) {
|
||||
if (command === "REQ") {
|
||||
const [name, ...filters] = data;
|
||||
sendMessage(ws, ["CLOSED", name, 'auth-required: you must authenticate first']);
|
||||
}
|
||||
if (command === "EVENT") {
|
||||
const [event] = data;
|
||||
sendMessage(ws, ["OK", event.id, false, 'auth-required: you must authenticate first']);
|
||||
}
|
||||
if (command === "AUTH") {
|
||||
const [event] = data;
|
||||
const valid = await validateAuthEvent(event, ws.data.authToken);
|
||||
if (!valid) return sendMessage(ws, ['NOTICE', "Invalid auth event"]);
|
||||
ws.data.authenticated = true;
|
||||
return;
|
||||
}
|
||||
return sendAuth(ws);
|
||||
}
|
||||
ws.data.remoteWs.send(msg);
|
||||
},
|
||||
open(ws) {
|
||||
sendAuth(ws);
|
||||
ws.data.remoteWs = new WebSocket(mainRelayUrl);
|
||||
ws.data.remoteWs.onmessage = (data) => ws.send(data.data, true);
|
||||
},
|
||||
perMessageDeflate: true,
|
||||
perMessageDeflateCompressionLevel: 9
|
||||
}
|
||||
})
|
||||
console.log('Server listening @', server.url.host)
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue