diff --git a/src/controllers/nostr/relay.ts b/src/controllers/nostr/relay.ts new file mode 100644 index 0000000..3aaa7b1 --- /dev/null +++ b/src/controllers/nostr/relay.ts @@ -0,0 +1,15 @@ +import type { AppController } from '@/app.ts'; + +const relayController: AppController = (c) => { + const upgrade = c.req.headers.get('upgrade'); + + if (upgrade?.toLowerCase() !== 'websocket') { + return c.text('Please use a Nostr client to connect.', 400); + } + + const { socket, response } = Deno.upgradeWebSocket(c.req.raw); + + return response; +}; + +export { relayController }; diff --git a/src/schemas/nostr.ts b/src/schemas/nostr.ts new file mode 100644 index 0000000..9f42b4f --- /dev/null +++ b/src/schemas/nostr.ts @@ -0,0 +1,25 @@ +import { z } from '@/deps.ts'; + +import { hexIdSchema, signedEventSchema } from '../schema.ts'; + +const filterSchema = z.object({ + kinds: z.number().int().positive().array().optional(), + ids: hexIdSchema.array().optional(), + authors: hexIdSchema.array().optional(), + since: z.number().int().positive().optional(), + until: z.number().int().positive().optional(), + limit: z.number().int().positive().optional(), +}).and(z.record( + z.custom<`#${string}`>((val) => typeof val === 'string' && val.startsWith('#')), + z.string().array(), +)); + +const clientMsgSchema = z.union([ + z.tuple([z.literal('REQ'), z.string().min(1)]).rest(filterSchema), + z.tuple([z.literal('EVENT'), signedEventSchema]), + z.tuple([z.literal('CLOSE'), z.string().min(1)]), +]); + +type Filter = z.infer; + +export { clientMsgSchema, filterSchema };