Move more Nostr schema stuff into schemas/nostr.ts

This commit is contained in:
Alex Gleason 2023-08-12 11:48:11 -05:00
parent 893542cf58
commit 80775d8bf0
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7
5 changed files with 35 additions and 48 deletions

View File

@ -1,7 +1,7 @@
import { type AppController } from '@/app.ts'; import { type AppController } from '@/app.ts';
import { type Filter, findReplyTag, z } from '@/deps.ts'; import { type Filter, findReplyTag, z } from '@/deps.ts';
import { getAuthor, getFilter, getFollows, publish } from '@/client.ts'; import { getAuthor, getFilter, getFollows, publish } from '@/client.ts';
import { parseMetaContent } from '@/schema.ts'; import { jsonMetaContentSchema } from '@/schemas/nostr.ts';
import { signEvent } from '@/sign.ts'; import { signEvent } from '@/sign.ts';
import { toAccount, toStatus } from '@/transformers/nostr-to-mastoapi.ts'; import { toAccount, toStatus } from '@/transformers/nostr-to-mastoapi.ts';
import { buildLinkHeader, eventDateComparator, lookupAccount, nostrNow, paginationSchema, parseBody } from '@/utils.ts'; import { buildLinkHeader, eventDateComparator, lookupAccount, nostrNow, paginationSchema, parseBody } from '@/utils.ts';
@ -154,7 +154,7 @@ const updateCredentialsController: AppController = async (c) => {
return c.json({ error: 'Could not find user.' }, 404); return c.json({ error: 'Could not find user.' }, 404);
} }
const meta = parseMetaContent(author); const meta = jsonMetaContentSchema.parse(author.content);
meta.name = result.data.display_name ?? meta.name; meta.name = result.data.display_name ?? meta.name;
meta.about = result.data.note ?? meta.about; meta.about = result.data.note ?? meta.about;

View File

@ -1,9 +1,5 @@
import { z } from '@/deps.ts'; import { z } from '@/deps.ts';
import type { Event } from './event.ts';
const optionalString = z.string().optional().catch(undefined);
/** Validates individual items in an array, dropping any that aren't valid. */ /** Validates individual items in an array, dropping any that aren't valid. */
function filteredArray<T extends z.ZodTypeAny>(schema: T) { function filteredArray<T extends z.ZodTypeAny>(schema: T) {
return z.any().array().catch([]) return z.any().array().catch([])
@ -24,31 +20,6 @@ const jsonSchema = z.string().transform((value, ctx) => {
} }
}); });
const metaContentSchema = z.object({
name: optionalString,
about: optionalString,
picture: optionalString,
banner: optionalString,
nip05: optionalString,
lud16: optionalString,
});
/** Author metadata from Event<0>. */
type MetaContent = z.infer<typeof metaContentSchema>;
/**
* Get (and validate) data from a kind 0 event.
* https://github.com/nostr-protocol/nips/blob/master/01.md
*/
function parseMetaContent(event: Event<0>): MetaContent {
try {
const json = JSON.parse(event.content);
return metaContentSchema.passthrough().parse(json);
} catch (_e) {
return {};
}
}
/** Alias for `safeParse`, but instead of returning a success object it returns the value (or undefined on fail). */ /** Alias for `safeParse`, but instead of returning a success object it returns the value (or undefined on fail). */
function parseValue<T>(schema: z.ZodType<T>, value: unknown): T | undefined { function parseValue<T>(schema: z.ZodType<T>, value: unknown): T | undefined {
const result = schema.safeParse(value); const result = schema.safeParse(value);
@ -83,15 +54,4 @@ const decode64Schema = z.string().transform((value, ctx) => {
const hashtagSchema = z.string().regex(/^\w{1,30}$/); const hashtagSchema = z.string().regex(/^\w{1,30}$/);
export { export { decode64Schema, emojiTagSchema, filteredArray, hashtagSchema, jsonSchema, parseRelay, relaySchema };
decode64Schema,
emojiTagSchema,
filteredArray,
hashtagSchema,
jsonSchema,
type MetaContent,
metaContentSchema,
parseMetaContent,
parseRelay,
relaySchema,
};

View File

@ -1,5 +1,7 @@
import { verifySignature, z } from '@/deps.ts'; import { verifySignature, z } from '@/deps.ts';
import { jsonSchema } from '../schema.ts';
/** Schema to validate Nostr hex IDs such as event IDs and pubkeys. */ /** Schema to validate Nostr hex IDs such as event IDs and pubkeys. */
const hexIdSchema = z.string().regex(/^[0-9a-f]{64}$/); const hexIdSchema = z.string().regex(/^[0-9a-f]{64}$/);
@ -37,4 +39,28 @@ const clientMsgSchema = z.union([
z.tuple([z.literal('CLOSE'), z.string().min(1)]), z.tuple([z.literal('CLOSE'), z.string().min(1)]),
]); ]);
export { clientMsgSchema, filterSchema, hexIdSchema, signedEventSchema }; /** Kind 0 content schema. */
const metaContentSchema = z.object({
name: z.string().optional().catch(undefined),
about: z.string().optional().catch(undefined),
picture: z.string().optional().catch(undefined),
banner: z.string().optional().catch(undefined),
nip05: z.string().optional().catch(undefined),
lud16: z.string().optional().catch(undefined),
}).partial().passthrough();
/** Parses kind 0 content from a JSON string. */
const jsonMetaContentSchema = jsonSchema.pipe(metaContentSchema).catch({});
/** Author metadata from Event<0>. */
type MetaContent = z.infer<typeof metaContentSchema>;
export {
clientMsgSchema,
filterSchema,
hexIdSchema,
jsonMetaContentSchema,
type MetaContent,
metaContentSchema,
signedEventSchema,
};

View File

@ -1,5 +1,5 @@
import { Conf } from '@/config.ts'; import { Conf } from '@/config.ts';
import { parseMetaContent } from '@/schema.ts'; import { jsonMetaContentSchema } from '@/schemas/nostr.ts';
import { getPublicKeyPem } from '@/utils/rsa.ts'; import { getPublicKeyPem } from '@/utils/rsa.ts';
import type { Event } from '@/event.ts'; import type { Event } from '@/event.ts';
@ -7,7 +7,7 @@ import type { Actor } from '@/schemas/activitypub.ts';
/** Nostr metadata event to ActivityPub actor. */ /** Nostr metadata event to ActivityPub actor. */
async function toActor(event: Event<0>, username: string): Promise<Actor | undefined> { async function toActor(event: Event<0>, username: string): Promise<Actor | undefined> {
const content = parseMetaContent(event); const content = jsonMetaContentSchema.parse(event.content);
return { return {
type: 'Person', type: 'Person',

View File

@ -6,7 +6,8 @@ import { findReplyTag, lodash, nip19, sanitizeHtml, TTLCache, unfurl, z } from '
import { type Event } from '@/event.ts'; import { type Event } from '@/event.ts';
import { verifyNip05Cached } from '@/nip05.ts'; import { verifyNip05Cached } from '@/nip05.ts';
import { getMediaLinks, type MediaLink, parseNoteContent } from '@/note.ts'; import { getMediaLinks, type MediaLink, parseNoteContent } from '@/note.ts';
import { emojiTagSchema, filteredArray, type MetaContent, parseMetaContent } from '@/schema.ts'; import { emojiTagSchema, filteredArray } from '@/schema.ts';
import { jsonMetaContentSchema } from '@/schemas/nostr.ts';
import { type Nip05, nostrDate, parseNip05, Time } from '@/utils.ts'; import { type Nip05, nostrDate, parseNip05, Time } from '@/utils.ts';
const DEFAULT_AVATAR = 'https://gleasonator.com/images/avi.png'; const DEFAULT_AVATAR = 'https://gleasonator.com/images/avi.png';
@ -20,7 +21,7 @@ async function toAccount(event: Event<0>, opts: ToAccountOpts = {}) {
const { withSource = false } = opts; const { withSource = false } = opts;
const { pubkey } = event; const { pubkey } = event;
const { name, nip05, picture, banner, about }: MetaContent = parseMetaContent(event); const { name, nip05, picture, banner, about } = jsonMetaContentSchema.parse(event.content);
const npub = nip19.npubEncode(pubkey); const npub = nip19.npubEncode(pubkey);
let parsed05: Nip05 | undefined; let parsed05: Nip05 | undefined;