Move more Nostr schema stuff into schemas/nostr.ts
This commit is contained in:
parent
893542cf58
commit
80775d8bf0
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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,
|
|
||||||
};
|
|
||||||
|
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue