Allow posting a status to Nostr
This commit is contained in:
parent
73884c212f
commit
2acfecc1eb
|
@ -1,17 +1,15 @@
|
|||
import { getPublicKey } from '@/deps.ts';
|
||||
|
||||
import { LOCAL_DOMAIN } from '../config.ts';
|
||||
import { fetchUser } from '../client.ts';
|
||||
import { MetaContent, metaContentSchema } from '../schema.ts';
|
||||
import { getKeys } from '../utils.ts';
|
||||
|
||||
import type { Context } from '@/deps.ts';
|
||||
|
||||
async function credentialsController(c: Context) {
|
||||
const authHeader = c.req.headers.get('Authorization') || '';
|
||||
const keys = getKeys(c);
|
||||
|
||||
if (authHeader.startsWith('Bearer ')) {
|
||||
const token = authHeader.split('Bearer ')[1];
|
||||
const pubkey = getPublicKey(token);
|
||||
if (keys) {
|
||||
const { pubkey } = keys;
|
||||
const event = await fetchUser(pubkey);
|
||||
const parsed = metaContentSchema.safeParse(JSON.parse(event?.content || ''));
|
||||
const content: MetaContent = parsed.success ? parsed.data : {};
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import { validator, z } from '@/deps.ts';
|
||||
|
||||
import publish from '../publisher.ts';
|
||||
import { toStatus } from '../transmute.ts';
|
||||
import { getKeys } from '../utils.ts';
|
||||
|
||||
import type { Event } from '../event.ts';
|
||||
|
||||
const createStatusSchema = z.object({
|
||||
status: z.string(),
|
||||
});
|
||||
|
||||
const createStatusController = validator('json', (value, c) => {
|
||||
const keys = getKeys(c);
|
||||
const result = createStatusSchema.safeParse(value);
|
||||
|
||||
if (result.success && keys) {
|
||||
const { data } = result;
|
||||
const { pubkey, privatekey } = keys;
|
||||
|
||||
const event: Event<1> = {
|
||||
kind: 1,
|
||||
pubkey: pubkey,
|
||||
content: data.status,
|
||||
tags: [],
|
||||
created_at: Math.floor(new Date().getTime() / 1000),
|
||||
};
|
||||
|
||||
publish(event, privatekey);
|
||||
return c.json(toStatus(event));
|
||||
} else {
|
||||
return c.json({ error: 'Bad request' }, 400);
|
||||
}
|
||||
});
|
||||
|
||||
export { createStatusController };
|
|
@ -5,6 +5,7 @@ import { appCredentialsController, createAppController } from './api/apps.ts';
|
|||
import { emptyArrayController } from './api/fallback.ts';
|
||||
import instanceController from './api/instance.ts';
|
||||
import { createTokenController } from './api/oauth.ts';
|
||||
import { createStatusController } from './api/statuses.ts';
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
|
@ -19,6 +20,8 @@ app.post('/oauth/token', createTokenController);
|
|||
|
||||
app.get('/api/v1/accounts/verify_credentials', credentialsController);
|
||||
|
||||
app.post('/api/v1/statuses', createStatusController);
|
||||
|
||||
// Not (yet) implemented.
|
||||
app.get('/api/v1/timelines/*', emptyArrayController);
|
||||
app.get('/api/v1/accounts/:id/statuses', emptyArrayController);
|
||||
|
|
|
@ -34,4 +34,4 @@ const fetchFollows = (pubkey: string): Promise<Event<3> | null> => {
|
|||
});
|
||||
};
|
||||
|
||||
export { fetchEvent, fetchFollows, fetchUser };
|
||||
export { fetchEvent, fetchFollows, fetchUser, pool };
|
||||
|
|
|
@ -2,3 +2,4 @@ export const LOCAL_DOMAIN = Deno.env.get('LOCAL_DOMAIN') || 'http://localhost:80
|
|||
export const POST_CHAR_LIMIT = Number(Deno.env.get('POST_CHAR_LIMIT') || 5000);
|
||||
|
||||
export const poolRelays = (Deno.env.get('RELAY_POOL') || '').split(',').filter(Boolean);
|
||||
export const publishRelays = ['wss://relay.mostr.pub'];
|
||||
|
|
|
@ -3,5 +3,5 @@ export { Hono, validator };
|
|||
export { cors } from 'https://deno.land/x/hono@v3.0.2/middleware.ts';
|
||||
export { z } from 'https://deno.land/x/zod@v3.20.5/mod.ts';
|
||||
export { Author, RelayPool } from 'https://dev.jspm.io/nostr-relaypool@0.5.3';
|
||||
export { getPublicKey } from 'https://dev.jspm.io/nostr-tools@1.6.0';
|
||||
export { getEventHash, getPublicKey, signEvent } from 'https://dev.jspm.io/nostr-tools@1.6.0';
|
||||
export type { Context };
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import { getEventHash, signEvent } from '@/deps.ts';
|
||||
|
||||
import { pool } from './client.ts';
|
||||
import { publishRelays } from './config.ts';
|
||||
|
||||
import type { Event } from './event.ts';
|
||||
|
||||
/** Publish an event to the Nostr relay. */
|
||||
function publish(event: Event, privatekey: string, relays = publishRelays): void {
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, privatekey);
|
||||
console.log('Publishing event', event);
|
||||
try {
|
||||
pool.publish(event, relays);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
export default publish;
|
|
@ -0,0 +1,39 @@
|
|||
import { LOCAL_DOMAIN } from './config.ts';
|
||||
|
||||
import type { Event } from './event.ts';
|
||||
|
||||
function toStatus(event: Event<1>) {
|
||||
return {
|
||||
id: event.id,
|
||||
account: {
|
||||
id: event.pubkey,
|
||||
},
|
||||
content: event.content,
|
||||
created_at: new Date(event.created_at * 1000).toISOString(),
|
||||
in_reply_to_id: null,
|
||||
in_reply_to_account_id: null,
|
||||
sensitive: false,
|
||||
spoiler_text: '',
|
||||
visibility: 'public',
|
||||
language: 'en',
|
||||
replies_count: 0,
|
||||
reblogs_count: 0,
|
||||
favourites_count: 0,
|
||||
favourited: false,
|
||||
reblogged: false,
|
||||
muted: false,
|
||||
bookmarked: false,
|
||||
reblog: null,
|
||||
application: null,
|
||||
media_attachments: [],
|
||||
mentions: [],
|
||||
tags: [],
|
||||
emojis: [],
|
||||
card: null,
|
||||
poll: null,
|
||||
uri: `${LOCAL_DOMAIN}/posts/${event.id}`,
|
||||
url: `${LOCAL_DOMAIN}/posts/${event.id}`,
|
||||
};
|
||||
}
|
||||
|
||||
export { toStatus };
|
|
@ -0,0 +1,17 @@
|
|||
import { Context, getPublicKey } from '@/deps.ts';
|
||||
|
||||
function getKeys(c: Context) {
|
||||
const auth = c.req.headers.get('Authorization') || '';
|
||||
|
||||
if (auth.startsWith('Bearer ')) {
|
||||
const privatekey = auth.split('Bearer ')[1];
|
||||
const pubkey = getPublicKey(privatekey);
|
||||
|
||||
return {
|
||||
privatekey,
|
||||
pubkey,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export { getKeys };
|
Loading…
Reference in New Issue