Add controller test, refactor some middlewares

This commit is contained in:
Alex Gleason 2025-02-10 12:41:41 -06:00
parent 1368304d25
commit 425edf2174
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
6 changed files with 58 additions and 15 deletions

View file

@ -0,0 +1,26 @@
// deno-lint-ignore-file require-await
import { NSecSigner } from '@nostrify/nostrify';
import { assertEquals } from '@std/assert';
import { generateSecretKey } from 'nostr-tools';
import { createTestDB } from '@/test.ts';
import cashuApp from './cashu.ts';
Deno.test('PUT /wallet', async () => {
await using db = await createTestDB();
const store = db.store;
const sk = generateSecretKey();
const signer = new NSecSigner(sk);
const app = cashuApp.use(
'*',
async (c) => c.set('store', store),
async (c) => c.set('signer', signer),
);
const response = await app.request('/wallet', { method: 'PUT' });
assertEquals(response.status, 200);
});

View file

@ -11,10 +11,10 @@ import { isNostrId } from '@/utils.ts';
import { createEvent, parseBody } from '@/utils/api.ts'; import { createEvent, parseBody } from '@/utils/api.ts';
import { errorJson } from '@/utils/log.ts'; import { errorJson } from '@/utils/log.ts';
import { signerMiddleware } from '@/middleware/signerMiddleware.ts'; import { signerMiddleware } from '@/middleware/signerMiddleware.ts';
import { requireSigner } from '@/middleware/requireSigner.ts'; import { requireNip44Signer } from '@/middleware/requireSigner.ts';
import { storeMiddleware } from '@/middleware/storeMiddleware.ts'; import { storeMiddleware } from '@/middleware/storeMiddleware.ts';
const app = new Hono(); const app = new Hono().use('*', storeMiddleware, signerMiddleware);
// CASHU_MINTS = ['https://mint.cashu.io/1', 'https://mint.cashu.io/2', 'https://mint.cashu.io/3'] // CASHU_MINTS = ['https://mint.cashu.io/1', 'https://mint.cashu.io/2', 'https://mint.cashu.io/3']
@ -55,7 +55,7 @@ const createCashuWalletSchema = z.object({
* Creates a replaceable Cashu wallet. * Creates a replaceable Cashu wallet.
* https://github.com/nostr-protocol/nips/blob/master/60.md * https://github.com/nostr-protocol/nips/blob/master/60.md
*/ */
app.post('/wallet', storeMiddleware, signerMiddleware, requireSigner, async (c) => { app.post('/wallet', requireNip44Signer, async (c) => {
const signer = c.get('signer'); const signer = c.get('signer');
const store = c.get('store'); const store = c.get('store');
const pubkey = await signer.getPublicKey(); const pubkey = await signer.getPublicKey();
@ -67,11 +67,6 @@ app.post('/wallet', storeMiddleware, signerMiddleware, requireSigner, async (c)
return c.json({ error: 'Bad schema', schema: result.error }, 400); return c.json({ error: 'Bad schema', schema: result.error }, 400);
} }
const nip44 = signer.nip44;
if (!nip44) {
return c.json({ error: 'Signer does not have nip 44' }, 400);
}
const [event] = await store.query([{ authors: [pubkey], kinds: [17375] }], { signal }); const [event] = await store.query([{ authors: [pubkey], kinds: [17375] }], { signal });
if (event) { if (event) {
return c.json({ error: 'You already have a wallet 😏' }, 400); return c.json({ error: 'You already have a wallet 😏' }, 400);
@ -90,7 +85,7 @@ app.post('/wallet', storeMiddleware, signerMiddleware, requireSigner, async (c)
contentTags.push(['mint', mint]); contentTags.push(['mint', mint]);
} }
const encryptedContentTags = await nip44.encrypt(pubkey, JSON.stringify(contentTags)); const encryptedContentTags = await signer.nip44.encrypt(pubkey, JSON.stringify(contentTags));
// Wallet // Wallet
await createEvent({ await createEvent({

View file

@ -1,6 +1,7 @@
import { MiddlewareHandler } from '@hono/hono'; import { MiddlewareHandler } from '@hono/hono';
import { HTTPException } from '@hono/hono/http-exception'; import { HTTPException } from '@hono/hono/http-exception';
import { NostrSigner } from '@nostrify/nostrify'; import { NostrSigner } from '@nostrify/nostrify';
import { SetRequired } from 'type-fest';
/** Throw a 401 if a signer isn't set. */ /** Throw a 401 if a signer isn't set. */
export const requireSigner: MiddlewareHandler<{ Variables: { signer: NostrSigner } }> = async (c, next) => { export const requireSigner: MiddlewareHandler<{ Variables: { signer: NostrSigner } }> = async (c, next) => {
@ -10,3 +11,19 @@ export const requireSigner: MiddlewareHandler<{ Variables: { signer: NostrSigner
await next(); await next();
}; };
/** Throw a 401 if a NIP-44 signer isn't set. */
export const requireNip44Signer: MiddlewareHandler<{ Variables: { signer: SetRequired<NostrSigner, 'nip44'> } }> =
async (c, next) => {
const signer = c.get('signer');
if (!signer) {
throw new HTTPException(401, { message: 'No pubkey provided' });
}
if (!signer.nip44) {
throw new HTTPException(401, { message: 'No NIP-44 signer provided' });
}
await next();
};

View file

@ -1,8 +1,8 @@
import { MiddlewareHandler } from '@hono/hono';
import { HTTPException } from '@hono/hono/http-exception'; import { HTTPException } from '@hono/hono/http-exception';
import { NSecSigner } from '@nostrify/nostrify'; import { NostrSigner, NSecSigner } from '@nostrify/nostrify';
import { nip19 } from 'nostr-tools'; import { nip19 } from 'nostr-tools';
import { AppMiddleware } from '@/app.ts';
import { Conf } from '@/config.ts'; import { Conf } from '@/config.ts';
import { ConnectSigner } from '@/signers/ConnectSigner.ts'; import { ConnectSigner } from '@/signers/ConnectSigner.ts';
import { ReadOnlySigner } from '@/signers/ReadOnlySigner.ts'; import { ReadOnlySigner } from '@/signers/ReadOnlySigner.ts';
@ -14,7 +14,7 @@ import { getTokenHash } from '@/utils/auth.ts';
const BEARER_REGEX = new RegExp(`^Bearer (${nip19.BECH32_REGEX.source})$`); const BEARER_REGEX = new RegExp(`^Bearer (${nip19.BECH32_REGEX.source})$`);
/** Make a `signer` object available to all controllers, or unset if the user isn't logged in. */ /** Make a `signer` object available to all controllers, or unset if the user isn't logged in. */
export const signerMiddleware: AppMiddleware = async (c, next) => { export const signerMiddleware: MiddlewareHandler<{ Variables: { signer: NostrSigner } }> = async (c, next) => {
const header = c.req.header('authorization'); const header = c.req.header('authorization');
const match = header?.match(BEARER_REGEX); const match = header?.match(BEARER_REGEX);

View file

@ -1,9 +1,14 @@
import { AppMiddleware } from '@/app.ts'; import { MiddlewareHandler } from '@hono/hono';
import { NostrSigner, NStore } from '@nostrify/nostrify';
import { UserStore } from '@/storages/UserStore.ts'; import { UserStore } from '@/storages/UserStore.ts';
import { Storages } from '@/storages.ts'; import { Storages } from '@/storages.ts';
/** Store middleware. */ /** Store middleware. */
export const storeMiddleware: AppMiddleware = async (c, next) => { export const storeMiddleware: MiddlewareHandler<{ Variables: { signer?: NostrSigner; store: NStore } }> = async (
c,
next,
) => {
const pubkey = await c.get('signer')?.getPublicKey(); const pubkey = await c.get('signer')?.getPublicKey();
if (pubkey) { if (pubkey) {

View file

@ -19,7 +19,7 @@ import { purifyEvent } from '@/utils/purify.ts';
type EventStub = TypeFest.SetOptional<EventTemplate, 'content' | 'created_at' | 'tags'>; type EventStub = TypeFest.SetOptional<EventTemplate, 'content' | 'created_at' | 'tags'>;
/** Publish an event through the pipeline. */ /** Publish an event through the pipeline. */
async function createEvent(t: EventStub, c: AppContext): Promise<NostrEvent> { async function createEvent(t: EventStub, c: Context): Promise<NostrEvent> {
const signer = c.get('signer'); const signer = c.get('signer');
if (!signer) { if (!signer) {