-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
15e7efd
commit 9e17017
Showing
15 changed files
with
644 additions
and
221 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,21 @@ | ||
// deno-lint-ignore-file | ||
import { typeDefs } from "./graphql-schema.ts"; | ||
import { SubscriptionMap, ws_handler } from "./ws.ts"; | ||
import Error404 from "./routes/_404.tsx"; | ||
import { render } from "https://esm.sh/[email protected]"; | ||
import { Mutation, RootResolver } from "./resolvers/root.ts"; | ||
import { RootResolver } from "./resolvers/root.ts"; | ||
import * as gql from "https://esm.sh/[email protected]"; | ||
|
||
import { get_policies, Policy, PolicyResolver } from "./resolvers/policy.ts"; | ||
import { Policy } from "./resolvers/policy.ts"; | ||
import { func_ResolvePolicyByKind } from "./resolvers/policy.ts"; | ||
import { func_GetEventsByKinds, func_WriteEvent } from "./resolvers/event.ts"; | ||
import { WriteEvent } from "./resolvers/event.ts"; | ||
import { func_GetEventsByIDs } from "./resolvers/event.ts"; | ||
import { GetEventsByIDs } from "./resolvers/event.ts"; | ||
import { GetEventsByKinds } from "./resolvers/event.ts"; | ||
import { EventStore, func_GetEventsByIDs } from "./resolvers/event.ts"; | ||
import { NostrEvent, NostrKind, parseJSON, PublicKey, verifyEvent } from "./_libs.ts"; | ||
|
||
import Dataloader from "https://esm.sh/[email protected]"; | ||
import { PolicyStore } from "./resolvers/policy.ts"; | ||
import { Policies } from "./resolvers/policy.ts"; | ||
import { interface_GetEventsByAuthors } from "./resolvers/event.ts"; | ||
import Landing from "./routes/landing.tsx"; | ||
import Error404 from "./routes/_404.tsx"; | ||
import { RelayInformation, RelayInformationStore } from "./resolvers/nip11.ts"; | ||
import { func_GetEventsByFilter } from "./resolvers/event.ts"; | ||
|
||
const schema = gql.buildSchema(gql.print(typeDefs)); | ||
|
||
|
@@ -26,20 +26,25 @@ export type DefaultPolicy = { | |
export type Relay = { | ||
server: Deno.HttpServer; | ||
url: string; | ||
password: string; | ||
shutdown: () => Promise<void>; | ||
set_policy: (args: { | ||
kind: NostrKind; | ||
read?: boolean | undefined; | ||
write?: boolean | undefined; | ||
}) => Promise<Policy>; | ||
block?: Set<string>; | ||
}) => Promise<Policy | Error>; | ||
get_policy: (kind: NostrKind) => Promise<Policy>; | ||
set_relay_information: (args: RelayInformation) => Promise<RelayInformation>; | ||
get_relay_information: () => Promise<RelayInformation>; | ||
default_policy: DefaultPolicy; | ||
}; | ||
|
||
export async function run(args: { | ||
port: number; | ||
admin?: PublicKey; | ||
password?: string; | ||
default_information?: RelayInformation; | ||
default_policy: DefaultPolicy; | ||
kv?: Deno.Kv; | ||
}): Promise<Error | Relay> { | ||
|
@@ -55,16 +60,21 @@ export async function run(args: { | |
args.kv = await Deno.openKv(); | ||
} | ||
|
||
const { port, default_policy } = args; | ||
const { port, default_policy, default_information } = args; | ||
|
||
let resolve_hostname; | ||
const hostname = new Promise<string>((resolve) => { | ||
resolve_hostname = resolve; | ||
}); | ||
|
||
const getter = get_policies(args.kv); | ||
// @ts-ignore | ||
const loader = new Dataloader<NostrKind, Policy | null>((kinds) => getter(kinds)); | ||
const get_all_policies = Policies(args.kv); | ||
const policyStore = new PolicyStore(default_policy, args.kv, await get_all_policies()); | ||
const relayInformationStore = new RelayInformationStore( | ||
args.kv, | ||
default_information, | ||
); | ||
|
||
const eventStore = await EventStore.New(args.kv); | ||
|
||
const server = Deno.serve( | ||
{ | ||
|
@@ -79,48 +89,30 @@ export async function run(args: { | |
...args, | ||
password, | ||
connections, | ||
resolvePolicyByKind: async (kind: NostrKind) => { | ||
const policy = await loader.load(kind); | ||
if (policy == 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(), | ||
}; | ||
} | ||
return policy; | ||
}, | ||
write_event: WriteEvent(args.kv), | ||
get_events_by_IDs: GetEventsByIDs(args.kv), | ||
get_events_by_kinds: GetEventsByKinds(args.kv), | ||
resolvePolicyByKind: policyStore.resolvePolicyByKind, | ||
write_event: eventStore.write_event.bind(eventStore), | ||
get_events_by_IDs: eventStore.get_events_by_IDs.bind(eventStore), | ||
get_events_by_kinds: eventStore.get_events_by_kinds.bind(eventStore), | ||
get_events_by_authors: eventStore.get_events_by_authors.bind(eventStore), | ||
get_events_by_filter: eventStore.get_events_by_filter.bind(eventStore), | ||
policyStore, | ||
relayInformationStore, | ||
kv: args.kv, | ||
}), | ||
); | ||
const resolvePolicyByKind = PolicyResolver(args.default_policy, args.kv); | ||
const mutation_resolver = Mutation({ ...args, resolvePolicyByKind, kv: args.kv }); | ||
|
||
return { | ||
server, | ||
password, | ||
url: `ws://${await hostname}:${port}`, | ||
shutdown: async () => { | ||
await server.shutdown(); | ||
args.kv?.close(); | ||
}, | ||
set_policy: mutation_resolver.set_policy, | ||
get_policy: (kind: NostrKind) => { | ||
return resolvePolicyByKind(kind); | ||
}, | ||
set_policy: policyStore.set_policy, | ||
get_policy: policyStore.resolvePolicyByKind, | ||
set_relay_information: relayInformationStore.set_relay_information, | ||
get_relay_information: relayInformationStore.resolveRelayInformation, | ||
default_policy: args.default_policy, | ||
}; | ||
} | ||
|
@@ -129,82 +121,111 @@ export type EventReadWriter = { | |
write_event: func_WriteEvent; | ||
get_events_by_IDs: func_GetEventsByIDs; | ||
get_events_by_kinds: func_GetEventsByKinds; | ||
}; | ||
get_events_by_filter: func_GetEventsByFilter; | ||
} & interface_GetEventsByAuthors; | ||
|
||
const root_handler = ( | ||
args: { | ||
password: string; | ||
information?: RelayInformation; | ||
connections: Map<WebSocket, SubscriptionMap>; | ||
default_policy: DefaultPolicy; | ||
resolvePolicyByKind: func_ResolvePolicyByKind; | ||
policyStore: PolicyStore; | ||
relayInformationStore: RelayInformationStore; | ||
kv: Deno.Kv; | ||
} & EventReadWriter, | ||
) => | ||
async (req: Request, info: Deno.ServeHandlerInfo) => { | ||
console.log(info.remoteAddr); | ||
|
||
const { pathname } = new URL(req.url); | ||
const { pathname, protocol } = new URL(req.url); | ||
if (pathname == "/api") { | ||
return graphql_handler(args)(req); | ||
} | ||
if (pathname == "/") { | ||
if (protocol == "http:" || protocol == "https:") { | ||
if (req.headers.get("accept")?.includes("text/html")) { | ||
return landing_handler(args); | ||
} | ||
if (req.headers.get("accept")?.includes("application/nostr+json")) { | ||
return information_handler(args); | ||
} | ||
} | ||
return ws_handler(args)(req, info); | ||
} | ||
const resp = new Response(render(Error404()), { status: 404 }); | ||
resp.headers.set("content-type", "html"); | ||
return resp; | ||
}; | ||
|
||
const graphql_handler = | ||
(args: { password: string; kv: Deno.Kv; resolvePolicyByKind: func_ResolvePolicyByKind }) => | ||
async (req: Request) => { | ||
const { password, kv, resolvePolicyByKind } = args; | ||
if (req.method == "POST") { | ||
const query = await req.json(); | ||
const nip42 = req.headers.get("nip42"); | ||
console.log("nip42 header", nip42); | ||
const graphql_handler = ( | ||
args: { | ||
password: string; | ||
kv: Deno.Kv; | ||
policyStore: PolicyStore; | ||
relayInformationStore: RelayInformationStore; | ||
}, | ||
) => | ||
async (req: Request) => { | ||
const { password, policyStore } = args; | ||
if (req.method == "POST") { | ||
const query = await req.json(); | ||
const nip42 = req.headers.get("nip42"); | ||
console.log("nip42 header", nip42); | ||
|
||
const pw = req.headers.get("password"); | ||
if (pw != password) { | ||
return new Response(`{"errors":"incorrect password"}`); | ||
} | ||
|
||
const pw = req.headers.get("password"); | ||
if (pw != password) { | ||
return new Response(`{"errors":"incorrect password"}`); | ||
if (nip42) { | ||
const auth_event = parseJSON<NostrEvent>(nip42); | ||
if (auth_event instanceof Error) { | ||
return new Response(`{errors:["no auth"]}`); | ||
} | ||
|
||
if (nip42) { | ||
const auth_event = parseJSON<NostrEvent>(nip42); | ||
if (auth_event instanceof Error) { | ||
return new Response(`{errors:["no auth"]}`); | ||
} | ||
const ok = await verifyEvent(auth_event); | ||
if (!ok) { | ||
return new Response(`{"errors":["no auth"]}`); | ||
} | ||
const ok = await verifyEvent(auth_event); | ||
if (!ok) { | ||
return new Response(`{"errors":["no auth"]}`); | ||
} | ||
const result = await gql.graphql({ | ||
schema: schema, | ||
source: query.query, | ||
variableValues: query.variables, | ||
rootValue: RootResolver(args), | ||
}); | ||
console.log(result); | ||
return new Response(JSON.stringify(result)); | ||
} else if (req.method == "GET") { | ||
const res = new Response(graphiql); | ||
res.headers.set("content-type", "html"); | ||
return res; | ||
} else { | ||
return new Response(undefined, { status: 405 }); | ||
} | ||
}; | ||
const result = await gql.graphql({ | ||
schema: schema, | ||
source: query.query, | ||
variableValues: query.variables, | ||
rootValue: RootResolver(args), | ||
}); | ||
console.log(result); | ||
return new Response(JSON.stringify(result)); | ||
} else if (req.method == "GET") { | ||
const res = new Response(graphiql); | ||
res.headers.set("content-type", "html"); | ||
return res; | ||
} else { | ||
return new Response(undefined, { status: 405 }); | ||
} | ||
}; | ||
|
||
export const supported_nips = [1, 2]; | ||
export const software = "https://github.com/BlowaterNostr/relayed"; | ||
|
||
export type RelayInformation = { | ||
name?: string; | ||
description?: string; | ||
pubkey?: string; | ||
contact?: string; | ||
supported_nips?: number[]; | ||
software?: string; | ||
version?: string; | ||
icon?: string; | ||
const landing_handler = async (args: { relayInformationStore: RelayInformationStore }) => { | ||
const resp = new Response( | ||
render(Landing(await args.relayInformationStore.resolveRelayInformation()), { status: 200 }), | ||
); | ||
resp.headers.set("content-type", "html"); | ||
return resp; | ||
}; | ||
|
||
const information_handler = async (args: { relayInformationStore: RelayInformationStore }) => { | ||
const resp = new Response(JSON.stringify(await args.relayInformationStore.resolveRelayInformation()), { | ||
status: 200, | ||
}); | ||
resp.headers.set("content-type", "application/json; charset=utf-8"); | ||
resp.headers.set("Access-Control-Allow-Origin", "*"); | ||
resp.headers.set("Access-Control-Allow-Methods", "GET"); | ||
resp.headers.set("Access-Control-Allow-Headers", "accept,content-type"); | ||
return resp; | ||
}; | ||
|
||
// export const kv = await Deno.openKv("./test-kv"); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
query getRelayInformation { | ||
relayInformation { | ||
name | ||
contact | ||
description | ||
icon | ||
pubkey | ||
software | ||
supported_nips | ||
version | ||
} | ||
} |
Oops, something went wrong.