Add account lookup endpoint
This commit is contained in:
parent
5118aaf46c
commit
9e73a8e43c
|
@ -1,7 +1,9 @@
|
||||||
import { type AppController } from '@/app.ts';
|
import { type AppController } from '@/app.ts';
|
||||||
|
import { nip19 } from '@/deps.ts';
|
||||||
|
|
||||||
import { getAuthor } from '../client.ts';
|
import { getAuthor } from '../client.ts';
|
||||||
import { toAccount } from '../transmute.ts';
|
import { toAccount } from '../transmute.ts';
|
||||||
|
import { bech32ToPubkey, isNostrId } from '../utils.ts';
|
||||||
|
|
||||||
const credentialsController: AppController = async (c) => {
|
const credentialsController: AppController = async (c) => {
|
||||||
const pubkey = c.get('pubkey')!;
|
const pubkey = c.get('pubkey')!;
|
||||||
|
@ -14,4 +16,42 @@ const credentialsController: AppController = async (c) => {
|
||||||
return c.json({ error: 'Could not find user.' }, 404);
|
return c.json({ error: 'Could not find user.' }, 404);
|
||||||
};
|
};
|
||||||
|
|
||||||
export { credentialsController };
|
const accountController: AppController = async (c) => {
|
||||||
|
const pubkey = c.req.param('id');
|
||||||
|
|
||||||
|
if (!isNostrId(pubkey)) {
|
||||||
|
return c.json({ error: 'Invalid account ID.' }, 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
const event = await getAuthor(pubkey);
|
||||||
|
if (event) {
|
||||||
|
return c.json(toAccount(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.json({ error: 'Could not find user.' }, 404);
|
||||||
|
};
|
||||||
|
|
||||||
|
const accountLookupController: AppController = async (c) => {
|
||||||
|
const acct = c.req.query('acct');
|
||||||
|
|
||||||
|
if (!acct) {
|
||||||
|
return c.json({ error: 'Missing `acct` query parameter.' }, 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (acct.includes('@')) {
|
||||||
|
// TODO: NIP-05 handling
|
||||||
|
return c.json({ error: 'NIP-05 lookups not yet implemented.' }, 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pubkey = bech32ToPubkey(acct);
|
||||||
|
if (pubkey) {
|
||||||
|
const event = await getAuthor(pubkey);
|
||||||
|
if (event) {
|
||||||
|
return c.json(toAccount(event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.json({ error: 'Could not find user.' }, 404);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { accountController, accountLookupController, credentialsController };
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { type Context, cors, type Handler, Hono, type HonoEnv, type MiddlewareHandler } from '@/deps.ts';
|
import { type Context, cors, type Handler, Hono, type HonoEnv, type MiddlewareHandler } from '@/deps.ts';
|
||||||
|
|
||||||
import { credentialsController } from './api/accounts.ts';
|
import { accountController, accountLookupController, credentialsController } from './api/accounts.ts';
|
||||||
import { appCredentialsController, createAppController } from './api/apps.ts';
|
import { appCredentialsController, createAppController } from './api/apps.ts';
|
||||||
import { emptyArrayController, emptyObjectController } from './api/fallback.ts';
|
import { emptyArrayController, emptyObjectController } from './api/fallback.ts';
|
||||||
import homeController from './api/home.ts';
|
import homeController from './api/home.ts';
|
||||||
|
@ -33,6 +33,8 @@ app.post('/oauth/token', createTokenController);
|
||||||
app.post('/oauth/revoke', emptyObjectController);
|
app.post('/oauth/revoke', emptyObjectController);
|
||||||
|
|
||||||
app.get('/api/v1/accounts/verify_credentials', requireAuth, credentialsController);
|
app.get('/api/v1/accounts/verify_credentials', requireAuth, credentialsController);
|
||||||
|
app.get('/api/v1/accounts/lookup', accountLookupController);
|
||||||
|
app.get('/api/v1/accounts/:id', accountController);
|
||||||
|
|
||||||
app.post('/api/v1/statuses', requireAuth, createStatusController);
|
app.post('/api/v1/statuses', requireAuth, createStatusController);
|
||||||
|
|
||||||
|
|
30
src/utils.ts
30
src/utils.ts
|
@ -1,4 +1,4 @@
|
||||||
import { Context, getPublicKey } from '@/deps.ts';
|
import { Context, getPublicKey, nip19, nip21 } from '@/deps.ts';
|
||||||
import { type Event } from '@/event.ts';
|
import { type Event } from '@/event.ts';
|
||||||
|
|
||||||
/** Get the current time in Nostr format. */
|
/** Get the current time in Nostr format. */
|
||||||
|
@ -21,4 +21,30 @@ function getKeys(c: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { eventDateComparator, getKeys, nostrNow };
|
/** Return true if the value is a bech32 string, eg for use with NIP-19. */
|
||||||
|
function isBech32(value: unknown): value is string {
|
||||||
|
return typeof value === 'string' && nip21.BECH32_REGEX.test(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return true if the value is a Nostr pubkey, private key, or event ID. */
|
||||||
|
function isNostrId(value: unknown): value is string {
|
||||||
|
return typeof value === 'string' && /^[0-9a-f]{64}$/.test(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get pubkey from bech32 string, if applicable. */
|
||||||
|
function bech32ToPubkey(bech32: string): string | undefined {
|
||||||
|
try {
|
||||||
|
const decoded = nip19.decode(bech32);
|
||||||
|
|
||||||
|
switch (decoded.type) {
|
||||||
|
case 'nprofile':
|
||||||
|
return decoded.data.pubkey;
|
||||||
|
case 'npub':
|
||||||
|
return decoded.data;
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { bech32ToPubkey, eventDateComparator, getKeys, isBech32, isNostrId, nostrNow };
|
||||||
|
|
Loading…
Reference in New Issue