Skip to content

Commit

Permalink
+
Browse files Browse the repository at this point in the history
  • Loading branch information
BlowaterNostr committed Mar 28, 2024
1 parent d1fc79a commit 0494e16
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 123 deletions.
19 changes: 16 additions & 3 deletions main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@ import { Mutation, RootResolver } from "./resolvers/root.ts";
import * as gql from "https://esm.sh/[email protected]";
import { parseJSON } from "./nostr.ts/_helper.ts";
import { PublicKey } from "./nostr.ts/key.ts";
import { NostrEvent, NostrFilters, verifyEvent } from "./nostr.ts/nostr.ts";
import { NostrEvent, NostrFilters, NostrKind, verifyEvent } from "./nostr.ts/nostr.ts";
import { PolicyResolver } from "./resolvers/policy.ts";

const schema = gql.buildSchema(gql.print(typeDefs));

export type DefaultPolicy = {
allowed_kinds: "all" | "none" | NostrKind[];
};

export async function run(args: {
port: number;
admin?: PublicKey;
password: string;
default_policy: DefaultPolicy;
}) {
const connections = new Map<WebSocket, Map<string, NostrFilters>>();

Expand All @@ -36,19 +42,26 @@ export async function run(args: {
connections,
}),
);
const resolvePolicyByKind = PolicyResolver(args.default_policy);
const mutation_resolver = Mutation(resolvePolicyByKind);
return {
server,
url: `ws://${await hostname}:${port}`,
shutdown: async () => {
await server.shutdown();
},
set_policy: Mutation.set_policy,
set_policy: mutation_resolver.set_policy,
get_policy: (kind: NostrKind) => {
return resolvePolicyByKind(kind);
},
default_policy: args.default_policy,
};
}

const root_handler = (args: {
password: string;
connections: Map<WebSocket, Map<string, NostrFilters>>;
default_policy: DefaultPolicy;
}) =>
async (req: Request, info: Deno.ServeHandlerInfo) => {
console.log(info.remoteAddr);
Expand All @@ -58,7 +71,7 @@ async (req: Request, info: Deno.ServeHandlerInfo) => {
return graphql_handler(password)(req);
}
if (pathname == "/") {
return ws_handler(connections)(req);
return ws_handler(args)(req);
}
const resp = new Response(render(Error404()), { status: 404 });
resp.headers.set("content-type", "html");
Expand Down
80 changes: 46 additions & 34 deletions resolvers/policy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { kv } from "../main.tsx";
import { DefaultPolicy, kv } from "../main.tsx";
import { NostrEvent, NostrKind } from "../nostr.ts/nostr.ts";

export async function Policies() {
Expand All @@ -10,43 +10,55 @@ export async function Policies() {
return res;
}

export async function PolicyResolver(kind: NostrKind): Promise<Policy> {
const entry = await kv.get<Policy>(["policy", kind]);
if (entry.value == null) {
return {
kind: kind,
read: true,
write: true,
allow: new Set(),
block: new Set(),
};
}
const policy = entry.value;
export type func_ResolvePolicyByKind = (kind: NostrKind) => Promise<Policy>;
export const PolicyResolver = (default_policy: DefaultPolicy): func_ResolvePolicyByKind =>
async function (kind: NostrKind): Promise<Policy> {
const entry = await kv.get<Policy>(["policy", kind]);
if (entry.value == null) {
let allow_this_kind: boolean;
if (default_policy.allowed_kinds == "all") {
allow_this_kind = true;
} else if (default_policy.allowed_kinds == "none") {
allow_this_kind = false;
} else if (default_policy.allowed_kinds.includes(kind)) {
allow_this_kind = true;
} else {
allow_this_kind = false;
}
return {
kind: kind,
read: allow_this_kind,
write: allow_this_kind,
allow: new Set(),
block: new Set(),
};
}
const policy = entry.value;

const allow = new Set<string>();
for (const item of policy.allow) {
if (typeof item == "string") {
allow.add(item);
const allow = new Set<string>();
for (const item of policy.allow) {
if (typeof item == "string") {
allow.add(item);
}
}
}
policy.allow = allow;
policy.allow = allow;

const block = new Set<string>();
for (const item of policy.block) {
if (typeof item == "string") {
block.add(item);
const block = new Set<string>();
for (const item of policy.block) {
if (typeof item == "string") {
block.add(item);
}
}
}
policy.block = block;
policy.kind = kind;
if (policy.read == null) {
policy.read = true;
}
if (policy.write == null) {
policy.write = true;
}
return policy;
}
policy.block = block;
policy.kind = kind;
if (policy.read == null) {
policy.read = true;
}
if (policy.write == null) {
policy.write = true;
}
return policy;
};

export type Policy = {
kind: NostrKind;
Expand Down
86 changes: 44 additions & 42 deletions resolvers/root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { kv } from "../main.tsx";
import { NostrKind } from "../nostr.ts/nostr.ts";
import { EventResolver } from "./event.ts";
import { EventsResolver } from "./event.ts";
import { Policies, PolicyResolver } from "./policy.ts";
import { func_ResolvePolicyByKind, Policies, PolicyResolver } from "./policy.ts";

const Query = {
events: EventsResolver,
Expand All @@ -12,48 +12,50 @@ const Query = {
policies: Policies,
};

export const Mutation = {
add_block: async (args: { kind: number; pubkey: string }) => {
const policy = await PolicyResolver(args.kind);
policy.block.add(args.pubkey);
await kv.set(["policy", args.kind], policy);
return policy;
},
remove_block: async (args: { kind: number; pubkey: string }) => {
const policy = await PolicyResolver(args.kind);
policy.block.delete(args.pubkey);
await kv.set(["policy", args.kind], policy);
return policy;
},
add_allow: async (args: { kind: number; pubkey: string }) => {
const policy = await PolicyResolver(args.kind);
policy.allow.add(args.pubkey);
await kv.set(["policy", args.kind], policy);
return policy;
},
remove_allow: async (args: { kind: number; pubkey: string }) => {
const policy = await PolicyResolver(args.kind);
policy.allow.delete(args.pubkey);
await kv.set(["policy", args.kind], policy);
return policy;
},
set_policy: async (
args: {
kind: NostrKind.TEXT_NOTE;
read: boolean | undefined;
write: boolean | undefined;
export const Mutation = (policyResolver: func_ResolvePolicyByKind) => {
return {
add_block: async (args: { kind: number; pubkey: string }) => {
const policy = await policyResolver(args.kind);
policy.block.add(args.pubkey);
await kv.set(["policy", args.kind], policy);
return policy;
},
) => {
const policy = await PolicyResolver(args.kind);
if (args.read != undefined) {
policy.read = args.read;
}
if (args.write != undefined) {
policy.write = args.write;
}
await kv.set(["policy", args.kind], policy);
return policy;
},
remove_block: async (args: { kind: number; pubkey: string }) => {
const policy = await policyResolver(args.kind);
policy.block.delete(args.pubkey);
await kv.set(["policy", args.kind], policy);
return policy;
},
add_allow: async (args: { kind: number; pubkey: string }) => {
const policy = await policyResolver(args.kind);
policy.allow.add(args.pubkey);
await kv.set(["policy", args.kind], policy);
return policy;
},
remove_allow: async (args: { kind: number; pubkey: string }) => {
const policy = await policyResolver(args.kind);
policy.allow.delete(args.pubkey);
await kv.set(["policy", args.kind], policy);
return policy;
},
set_policy: async (
args: {
kind: NostrKind.TEXT_NOTE;
read: boolean | undefined;
write: boolean | undefined;
},
) => {
const policy = await policyResolver(args.kind);
if (args.read != undefined) {
policy.read = args.read;
}
if (args.write != undefined) {
policy.write = args.write;
}
await kv.set(["policy", args.kind], policy);
return policy;
},
};
};

export function RootResolver() {
Expand Down
51 changes: 31 additions & 20 deletions test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
import {
close_sub_keep_reading,
newSub_close,
open_close,
send_event,
sub_exits,
} from "./nostr.ts/relay-single-test.ts";
import { run } from "./main.tsx";
import { RelayRejectedEvent, SingleRelayConnection } from "./nostr.ts/relay-single.ts";
import { prepareNormalNostrEvent } from "./nostr.ts/event.ts";
Expand All @@ -16,28 +9,38 @@ Deno.test("localhost", async () => {
const relay = await run({
password: "123",
port: 8080,
default_policy: {
allowed_kinds: "none",
},
});

const localhost = relay.url;

// client logic
await open_close([localhost])();
await newSub_close(localhost)();
await sub_exits(localhost)();
await close_sub_keep_reading(localhost)();
await send_event(localhost)();
const policy = await relay.get_policy(NostrKind.CONTACTS);
assertEquals(policy, {
allow: new Set(),
block: new Set(),
kind: NostrKind.CONTACTS,
read: false,
write: false,
});

// relay logic
const ctx = InMemoryAccountContext.Generate();
const connection = SingleRelayConnection.New(relay.url);
const client = SingleRelayConnection.New(relay.url, { log: false });

{
// because default policy allows no kinds
const err = await client.sendEvent(await randomeEvent(ctx));
assertEquals(err instanceof Error, true);
console.log("send", err);
}

{
await relay.set_policy({
kind: NostrKind.TEXT_NOTE,
read: false,
write: false,
});
const err = await connection.sendEvent(
const err = await client.sendEvent(
await prepareNormalNostrEvent(ctx, {
kind: NostrKind.TEXT_NOTE,
content: "",
Expand All @@ -55,13 +58,21 @@ Deno.test("localhost", async () => {
kind: NostrKind.TEXT_NOTE,
content: "",
});
const err = await connection.sendEvent(event);
const err = await client.sendEvent(event);
if (err instanceof Error) fail(err.message);

const event_get = await connection.getEvent(event.id);
const event_get = await client.getEvent(event.id);
console.log("get", event_get);
}

await connection.close();
await client.close();
await relay.shutdown();
});

async function randomeEvent(ctx: InMemoryAccountContext) {
const event = await prepareNormalNostrEvent(ctx, {
kind: NostrKind.TEXT_NOTE,
content: "",
});
return event;
}
Loading

0 comments on commit 0494e16

Please sign in to comment.