From 7d877b5a37b5802ecadd21515f5add997fa04f59 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 13 Oct 2024 17:27:39 -0500 Subject: [PATCH] Refactor accountsController into a separate route --- src/app.ts | 42 +------ src/controllers/api/accounts.ts | 170 ++++++++------------------- src/controllers/api/favourites.ts | 35 ++++++ src/middleware/requireSigner.ts | 6 +- src/middleware/uploaderMiddleware.ts | 79 +++++++------ src/utils/api.ts | 25 ++-- src/utils/upload.ts | 5 +- 7 files changed, 151 insertions(+), 211 deletions(-) create mode 100644 src/controllers/api/favourites.ts diff --git a/src/app.ts b/src/app.ts index ad80cbb1..d37c2fd0 100644 --- a/src/app.ts +++ b/src/app.ts @@ -9,26 +9,7 @@ import '@/startup.ts'; import { Time } from '@/utils/time.ts'; -import { - accountController, - accountLookupController, - accountSearchController, - accountStatusesController, - blockController, - createAccountController, - familiarFollowersController, - favouritesController, - followController, - followersController, - followingController, - muteController, - relationshipsController, - unblockController, - unfollowController, - unmuteController, - updateCredentialsController, - verifyCredentialsController, -} from '@/controllers/api/accounts.ts'; +import { accountsController } from '@/controllers/api/accounts.ts'; import { adminAccountsController, adminActionController, @@ -50,6 +31,7 @@ import { updateZapSplitsController, } from '@/controllers/api/ditto.ts'; import { emptyArrayController, emptyObjectController, notImplementedController } from '@/controllers/api/fallback.ts'; +import { favouritesController } from '@/controllers/api/favourites.ts'; import { instanceDescriptionController, instanceV1Controller, @@ -200,23 +182,7 @@ app.post('/oauth/revoke', emptyObjectController); app.post('/oauth/authorize', oauthAuthorizeController); app.get('/oauth/authorize', oauthController); -app.post('/api/v1/accounts', requireProof({ pow: 20 }), createAccountController); -app.get('/api/v1/accounts/verify_credentials', requireSigner, verifyCredentialsController); -app.patch('/api/v1/accounts/update_credentials', requireSigner, updateCredentialsController); -app.get('/api/v1/accounts/search', accountSearchController); -app.get('/api/v1/accounts/lookup', accountLookupController); -app.get('/api/v1/accounts/relationships', requireSigner, relationshipsController); -app.get('/api/v1/accounts/familiar_followers', requireSigner, familiarFollowersController); -app.post('/api/v1/accounts/:pubkey{[0-9a-f]{64}}/block', requireSigner, blockController); -app.post('/api/v1/accounts/:pubkey{[0-9a-f]{64}}/unblock', requireSigner, unblockController); -app.post('/api/v1/accounts/:pubkey{[0-9a-f]{64}}/mute', requireSigner, muteController); -app.post('/api/v1/accounts/:pubkey{[0-9a-f]{64}}/unmute', requireSigner, unmuteController); -app.post('/api/v1/accounts/:pubkey{[0-9a-f]{64}}/follow', requireSigner, followController); -app.post('/api/v1/accounts/:pubkey{[0-9a-f]{64}}/unfollow', requireSigner, unfollowController); -app.get('/api/v1/accounts/:pubkey{[0-9a-f]{64}}/followers', followersController); -app.get('/api/v1/accounts/:pubkey{[0-9a-f]{64}}/following', followingController); -app.get('/api/v1/accounts/:pubkey{[0-9a-f]{64}}/statuses', accountStatusesController); -app.get('/api/v1/accounts/:pubkey{[0-9a-f]{64}}', accountController); +app.route('/api/v1/accounts', accountsController); app.get('/api/v1/statuses/:id{[0-9a-f]{64}}/favourited_by', favouritedByController); app.get('/api/v1/statuses/:id{[0-9a-f]{64}}/reblogged_by', rebloggedByController); @@ -268,7 +234,7 @@ app.get('/api/v1/suggestions', suggestionsV1Controller); app.get('/api/v2/suggestions', suggestionsV2Controller); app.get('/api/v1/notifications', requireSigner, notificationsController); -app.get('/api/v1/favourites', requireSigner, favouritesController); +app.route('/api/v1/favourites', favouritesController); app.get('/api/v1/bookmarks', requireSigner, bookmarksController); app.get('/api/v1/blocks', requireSigner, blocksController); app.get('/api/v1/mutes', requireSigner, mutesController); diff --git a/src/controllers/api/accounts.ts b/src/controllers/api/accounts.ts index b3c75ae5..266fcc6b 100644 --- a/src/controllers/api/accounts.ts +++ b/src/controllers/api/accounts.ts @@ -1,14 +1,12 @@ +import { Hono } from '@hono/hono'; import { NostrFilter, NSchema as n } from '@nostrify/nostrify'; -import { nip19 } from 'nostr-tools'; import { z } from 'zod'; -import { type AppController } from '@/app.ts'; import { Conf } from '@/config.ts'; import { getAuthor, getFollowedPubkeys } from '@/queries.ts'; import { booleanParamSchema, fileSchema } from '@/schema.ts'; import { Storages } from '@/storages.ts'; import { uploadFile } from '@/utils/upload.ts'; -import { nostrNow } from '@/utils.ts'; import { createEvent, paginated, parseBody, updateEvent, updateListEvent } from '@/utils/api.ts'; import { extractIdentifier, lookupAccount, lookupPubkey } from '@/utils/lookup.ts'; import { renderAccounts, renderEventAccounts, renderStatuses } from '@/views.ts'; @@ -19,34 +17,17 @@ import { hydrateEvents } from '@/storages/hydrate.ts'; import { bech32ToPubkey } from '@/utils.ts'; import { addTag, deleteTag, findReplyTag, getTagSet } from '@/utils/tags.ts'; import { getPubkeysBySearch } from '@/utils/search.ts'; +import { requireSigner } from '@/middleware/requireSigner.ts'; +import { paginationMiddleware } from '@/middleware/paginationMiddleware.ts'; -const usernameSchema = z - .string().min(1).max(30) - .regex(/^[a-z0-9_]+$/i) - .refine((username) => !Conf.forbiddenUsernames.includes(username), 'Username is reserved.'); +export const accountsController = new Hono(); -const createAccountSchema = z.object({ - username: usernameSchema, +accountsController.post('/', (c) => { + return c.json({ error: 'Please create an account on the web first' }, 422); }); -const createAccountController: AppController = async (c) => { - const pubkey = await c.get('signer')?.getPublicKey()!; - const result = createAccountSchema.safeParse(await c.req.json()); - - if (!result.success) { - return c.json({ error: 'Bad request', schema: result.error }, 400); - } - - return c.json({ - access_token: nip19.npubEncode(pubkey), - token_type: 'Bearer', - scope: 'read write follow push', - created_at: nostrNow(), - }); -}; - -const verifyCredentialsController: AppController = async (c) => { - const signer = c.get('signer')!; +accountsController.get('/verify_credentials', requireSigner, async (c) => { + const signer = c.get('signer'); const pubkey = await signer.getPublicKey(); const store = await Storages.db(); @@ -74,9 +55,9 @@ const verifyCredentialsController: AppController = async (c) => { : await accountFromPubkey(pubkey, { withSource: true, settingsStore }); return c.json(account); -}; +}); -const accountController: AppController = async (c) => { +accountsController.get('/:pubkey{[0-9a-f]{64}}', async (c) => { const pubkey = c.req.param('pubkey'); const event = await getAuthor(pubkey); @@ -85,9 +66,9 @@ const accountController: AppController = async (c) => { } else { return c.json(await accountFromPubkey(pubkey)); } -}; +}); -const accountLookupController: AppController = async (c) => { +accountsController.get('/lookup', async (c) => { const acct = c.req.query('acct'); if (!acct) { @@ -104,7 +85,7 @@ const accountLookupController: AppController = async (c) => { } catch { return c.json({ error: 'Could not find user.' }, 404); } -}; +}); const accountSearchQuerySchema = z.object({ q: z.string().transform(decodeURIComponent), @@ -112,7 +93,7 @@ const accountSearchQuerySchema = z.object({ following: z.boolean().default(false), }); -const accountSearchController: AppController = async (c) => { +accountsController.get('/search', paginationMiddleware, async (c) => { const { signal } = c.req.raw; const { limit } = c.get('pagination'); const kysely = await Storages.kysely(); @@ -155,10 +136,10 @@ const accountSearchController: AppController = async (c) => { ); return c.json(accounts); -}; +}); -const relationshipsController: AppController = async (c) => { - const pubkey = await c.get('signer')?.getPublicKey()!; +accountsController.get('/relationships', requireSigner, async (c) => { + const pubkey = await c.get('signer').getPublicKey(); const ids = z.array(z.string()).safeParse(c.req.queries('id[]')); if (!ids.success) { @@ -186,7 +167,7 @@ const relationshipsController: AppController = async (c) => { ); return c.json(result); -}; +}); const accountStatusesQuerySchema = z.object({ pinned: booleanParamSchema.optional(), @@ -195,7 +176,7 @@ const accountStatusesQuerySchema = z.object({ tagged: z.string().optional(), }); -const accountStatusesController: AppController = async (c) => { +accountsController.get('/:pubkey{[0-9a-f]{64}}/statuses', paginationMiddleware, async (c) => { const pubkey = c.req.param('pubkey'); const { since, until } = c.get('pagination'); const { pinned, limit, exclude_replies, tagged } = accountStatusesQuerySchema.parse(c.req.query()); @@ -254,8 +235,9 @@ const accountStatusesController: AppController = async (c) => { return renderStatus(event, { viewerPubkey }); }), ); + return paginated(c, events, statuses); -}; +}); const updateCredentialsSchema = z.object({ display_name: z.string().optional(), @@ -271,8 +253,8 @@ const updateCredentialsSchema = z.object({ website: z.string().url().or(z.literal('')).optional(), }); -const updateCredentialsController: AppController = async (c) => { - const signer = c.get('signer')!; +accountsController.patch('/update_credentials', requireSigner, async (c) => { + const signer = c.get('signer'); const pubkey = await signer.getPublicKey(); const body = await parseBody(c.req.raw); const result = updateCredentialsSchema.safeParse(body); @@ -337,11 +319,11 @@ const updateCredentialsController: AppController = async (c) => { } return c.json(account); -}; +}); -/** https://docs.joinmastodon.org/methods/accounts/#follow */ -const followController: AppController = async (c) => { - const sourcePubkey = await c.get('signer')?.getPublicKey()!; +// https://docs.joinmastodon.org/methods/accounts/#follow +accountsController.post('/:pubkey{[0-9a-f]{64}}/follow', requireSigner, async (c) => { + const sourcePubkey = await c.get('signer').getPublicKey(); const targetPubkey = c.req.param('pubkey'); await updateListEvent( @@ -354,10 +336,10 @@ const followController: AppController = async (c) => { relationship.following = true; return c.json(relationship); -}; +}); -/** https://docs.joinmastodon.org/methods/accounts/#unfollow */ -const unfollowController: AppController = async (c) => { +// https://docs.joinmastodon.org/methods/accounts/#unfollow +accountsController.post('/:pubkey{[0-9a-f]{64}}/unfollow', requireSigner, async (c) => { const sourcePubkey = await c.get('signer')?.getPublicKey()!; const targetPubkey = c.req.param('pubkey'); @@ -369,33 +351,33 @@ const unfollowController: AppController = async (c) => { const relationship = await getRelationship(sourcePubkey, targetPubkey); return c.json(relationship); -}; +}); -const followersController: AppController = (c) => { +accountsController.get('/:pubkey{[0-9a-f]{64}}/followers', paginationMiddleware, (c) => { const pubkey = c.req.param('pubkey'); const params = c.get('pagination'); return renderEventAccounts(c, [{ kinds: [3], '#p': [pubkey], ...params }]); -}; +}); -const followingController: AppController = async (c) => { +accountsController.get('/:pubkey{[0-9a-f]{64}}/following', paginationMiddleware, async (c) => { const pubkey = c.req.param('pubkey'); const pubkeys = await getFollowedPubkeys(pubkey); return renderAccounts(c, [...pubkeys]); -}; +}); -/** https://docs.joinmastodon.org/methods/accounts/#block */ -const blockController: AppController = (c) => { +// https://docs.joinmastodon.org/methods/accounts/#block +accountsController.post('/:pubkey{[0-9a-f]{64}}/block', (c) => { return c.json({ error: 'Blocking is not supported by Nostr' }, 422); -}; +}); -/** https://docs.joinmastodon.org/methods/accounts/#unblock */ -const unblockController: AppController = (c) => { +// https://docs.joinmastodon.org/methods/accounts/#unblock +accountsController.post('/:pubkey{[0-9a-f]{64}}/unblock', (c) => { return c.json({ error: 'Blocking is not supported by Nostr' }, 422); -}; +}); -/** https://docs.joinmastodon.org/methods/accounts/#mute */ -const muteController: AppController = async (c) => { - const sourcePubkey = await c.get('signer')?.getPublicKey()!; +// https://docs.joinmastodon.org/methods/accounts/#mute +accountsController.post('/:pubkey{[0-9a-f]{64}}/mute', requireSigner, async (c) => { + const sourcePubkey = await c.get('signer').getPublicKey(); const targetPubkey = c.req.param('pubkey'); await updateListEvent( @@ -406,11 +388,11 @@ const muteController: AppController = async (c) => { const relationship = await getRelationship(sourcePubkey, targetPubkey); return c.json(relationship); -}; +}); -/** https://docs.joinmastodon.org/methods/accounts/#unmute */ -const unmuteController: AppController = async (c) => { - const sourcePubkey = await c.get('signer')?.getPublicKey()!; +// https://docs.joinmastodon.org/methods/accounts/#unmute +accountsController.post('/:pubkey{[0-9a-f]{64}}/unmute', requireSigner, async (c) => { + const sourcePubkey = await c.get('signer').getPublicKey(); const targetPubkey = c.req.param('pubkey'); await updateListEvent( @@ -421,38 +403,11 @@ const unmuteController: AppController = async (c) => { const relationship = await getRelationship(sourcePubkey, targetPubkey); return c.json(relationship); -}; - -const favouritesController: AppController = async (c) => { - const pubkey = await c.get('signer')?.getPublicKey()!; - const params = c.get('pagination'); - const { signal } = c.req.raw; +}); +accountsController.get('/familiar_followers', requireSigner, async (c) => { const store = await Storages.db(); - - const events7 = await store.query( - [{ kinds: [7], authors: [pubkey], ...params }], - { signal }, - ); - - const ids = events7 - .map((event) => event.tags.find((tag) => tag[0] === 'e')?.[1]) - .filter((id): id is string => !!id); - - const events1 = await store.query([{ kinds: [1], ids }], { signal }) - .then((events) => hydrateEvents({ events, store, signal })); - - const viewerPubkey = await c.get('signer')?.getPublicKey(); - - const statuses = await Promise.all( - events1.map((event) => renderStatus(event, { viewerPubkey })), - ); - return paginated(c, events1, statuses); -}; - -const familiarFollowersController: AppController = async (c) => { - const store = await Storages.db(); - const signer = c.get('signer')!; + const signer = c.get('signer'); const pubkey = await signer.getPublicKey(); const ids = z.array(z.string()).parse(c.req.queries('id[]')); @@ -470,7 +425,7 @@ const familiarFollowersController: AppController = async (c) => { })); return c.json(results); -}; +}); async function getRelationship(sourcePubkey: string, targetPubkey: string) { const db = await Storages.db(); @@ -488,24 +443,3 @@ async function getRelationship(sourcePubkey: string, targetPubkey: string) { event10000: sourceEvents.find((event) => event.kind === 10000 && event.pubkey === sourcePubkey), }); } - -export { - accountController, - accountLookupController, - accountSearchController, - accountStatusesController, - blockController, - createAccountController, - familiarFollowersController, - favouritesController, - followController, - followersController, - followingController, - muteController, - relationshipsController, - unblockController, - unfollowController, - unmuteController, - updateCredentialsController, - verifyCredentialsController, -}; diff --git a/src/controllers/api/favourites.ts b/src/controllers/api/favourites.ts new file mode 100644 index 00000000..9a294dfc --- /dev/null +++ b/src/controllers/api/favourites.ts @@ -0,0 +1,35 @@ +import { Hono } from '@hono/hono'; + +import { paginationMiddleware } from '@/middleware/paginationMiddleware.ts'; +import { requireSigner } from '@/middleware/requireSigner.ts'; +import { Storages } from '@/storages.ts'; +import { hydrateEvents } from '@/storages/hydrate.ts'; +import { paginated } from '@/utils/api.ts'; +import { renderStatus } from '@/views/mastodon/statuses.ts'; + +export const favouritesController = new Hono(); + +favouritesController.get('/', paginationMiddleware, requireSigner, async (c) => { + const pubkey = await c.get('signer').getPublicKey(); + const params = c.get('pagination'); + const { signal } = c.req.raw; + + const store = await Storages.db(); + + const events7 = await store.query( + [{ kinds: [7], authors: [pubkey], ...params }], + { signal }, + ); + + const ids = events7 + .map((event) => event.tags.find((tag) => tag[0] === 'e')?.[1]) + .filter((id): id is string => !!id); + + const events1 = await store.query([{ kinds: [1], ids }], { signal }) + .then((events) => hydrateEvents({ events, store, signal })); + + const statuses = await Promise.all( + events1.map((event) => renderStatus(event, { viewerPubkey: pubkey })), + ); + return paginated(c, events1, statuses); +}); diff --git a/src/middleware/requireSigner.ts b/src/middleware/requireSigner.ts index c954dbd6..e360ab42 100644 --- a/src/middleware/requireSigner.ts +++ b/src/middleware/requireSigner.ts @@ -1,9 +1,9 @@ +import { MiddlewareHandler } from '@hono/hono'; import { HTTPException } from '@hono/hono/http-exception'; - -import { AppMiddleware } from '@/app.ts'; +import { NostrSigner } from '@nostrify/nostrify'; /** Throw a 401 if a signer isn't set. */ -export const requireSigner: AppMiddleware = async (c, next) => { +export const requireSigner: MiddlewareHandler<{ Variables: { signer: NostrSigner } }> = async (c, next) => { if (!c.get('signer')) { throw new HTTPException(401, { message: 'No pubkey provided' }); } diff --git a/src/middleware/uploaderMiddleware.ts b/src/middleware/uploaderMiddleware.ts index 96a47336..8ce61994 100644 --- a/src/middleware/uploaderMiddleware.ts +++ b/src/middleware/uploaderMiddleware.ts @@ -1,6 +1,7 @@ +import { MiddlewareHandler } from '@hono/hono'; +import { type NostrSigner, NUploader } from '@nostrify/nostrify'; import { BlossomUploader, NostrBuildUploader } from '@nostrify/nostrify/uploaders'; -import { AppMiddleware } from '@/app.ts'; import { Conf } from '@/config.ts'; import { DenoUploader } from '@/uploaders/DenoUploader.ts'; import { IPFSUploader } from '@/uploaders/IPFSUploader.ts'; @@ -8,41 +9,45 @@ import { S3Uploader } from '@/uploaders/S3Uploader.ts'; import { fetchWorker } from '@/workers/fetch.ts'; /** Set an uploader for the user. */ -export const uploaderMiddleware: AppMiddleware = async (c, next) => { - const signer = c.get('signer'); +export const uploaderMiddleware: MiddlewareHandler<{ Variables: { signer?: NostrSigner; uploader?: NUploader } }> = + async (c, next) => { + const signer = c.get('signer'); - switch (Conf.uploader) { - case 's3': - c.set( - 'uploader', - new S3Uploader({ - accessKey: Conf.s3.accessKey, - bucket: Conf.s3.bucket, - endPoint: Conf.s3.endPoint!, - pathStyle: Conf.s3.pathStyle, - port: Conf.s3.port, - region: Conf.s3.region!, - secretKey: Conf.s3.secretKey, - sessionToken: Conf.s3.sessionToken, - useSSL: Conf.s3.useSSL, - }), - ); - break; - case 'ipfs': - c.set('uploader', new IPFSUploader({ baseUrl: Conf.mediaDomain, apiUrl: Conf.ipfs.apiUrl, fetch: fetchWorker })); - break; - case 'local': - c.set('uploader', new DenoUploader({ baseUrl: Conf.mediaDomain, dir: Conf.uploadsDir })); - break; - case 'nostrbuild': - c.set('uploader', new NostrBuildUploader({ endpoint: Conf.nostrbuildEndpoint, signer, fetch: fetchWorker })); - break; - case 'blossom': - if (signer) { - c.set('uploader', new BlossomUploader({ servers: Conf.blossomServers, signer, fetch: fetchWorker })); - } - break; - } + switch (Conf.uploader) { + case 's3': + c.set( + 'uploader', + new S3Uploader({ + accessKey: Conf.s3.accessKey, + bucket: Conf.s3.bucket, + endPoint: Conf.s3.endPoint!, + pathStyle: Conf.s3.pathStyle, + port: Conf.s3.port, + region: Conf.s3.region!, + secretKey: Conf.s3.secretKey, + sessionToken: Conf.s3.sessionToken, + useSSL: Conf.s3.useSSL, + }), + ); + break; + case 'ipfs': + c.set( + 'uploader', + new IPFSUploader({ baseUrl: Conf.mediaDomain, apiUrl: Conf.ipfs.apiUrl, fetch: fetchWorker }), + ); + break; + case 'local': + c.set('uploader', new DenoUploader({ baseUrl: Conf.mediaDomain, dir: Conf.uploadsDir })); + break; + case 'nostrbuild': + c.set('uploader', new NostrBuildUploader({ endpoint: Conf.nostrbuildEndpoint, signer, fetch: fetchWorker })); + break; + case 'blossom': + if (signer) { + c.set('uploader', new BlossomUploader({ servers: Conf.blossomServers, signer, fetch: fetchWorker })); + } + break; + } - await next(); -}; + await next(); + }; diff --git a/src/utils/api.ts b/src/utils/api.ts index e7766979..90c01878 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -6,7 +6,6 @@ import { parseFormData } from 'formdata-helper'; import { EventTemplate } from 'nostr-tools'; import * as TypeFest from 'type-fest'; -import { type AppContext } from '@/app.ts'; import { Conf } from '@/config.ts'; import * as pipeline from '@/pipeline.ts'; import { RelayError } from '@/RelayError.ts'; @@ -21,7 +20,7 @@ const debug = Debug('ditto:api'); type EventStub = TypeFest.SetOptional; /** Publish an event through the pipeline. */ -async function createEvent(t: EventStub, c: AppContext): Promise { +async function createEvent(t: EventStub, c: Context): Promise { const signer = c.get('signer'); if (!signer) { @@ -50,7 +49,7 @@ interface UpdateEventFilter extends NostrFilter { async function updateEvent( filter: UpdateEventFilter, fn: (prev: NostrEvent) => E | Promise, - c: AppContext, + c: Context, ): Promise { const store = await Storages.db(); @@ -72,7 +71,7 @@ async function updateEvent( function updateListEvent( filter: UpdateEventFilter, fn: (tags: string[][]) => string[][], - c: AppContext, + c: Context, ): Promise { return updateEvent(filter, ({ content, tags }) => ({ kind: filter.kinds[0], @@ -82,7 +81,7 @@ function updateListEvent( } /** Publish an admin event through the pipeline. */ -async function createAdminEvent(t: EventStub, c: AppContext): Promise { +async function createAdminEvent(t: EventStub, c: Context): Promise { const signer = new AdminSigner(); const event = await signer.signEvent({ @@ -99,7 +98,7 @@ async function createAdminEvent(t: EventStub, c: AppContext): Promise string[][], - c: AppContext, + c: Context, ): Promise { return updateAdminEvent(filter, (prev) => ({ kind: filter.kinds[0], @@ -112,22 +111,22 @@ function updateListAdminEvent( async function updateAdminEvent( filter: UpdateEventFilter, fn: (prev: NostrEvent | undefined) => E, - c: AppContext, + c: Context, ): Promise { const store = await Storages.db(); const [prev] = await store.query([filter], { limit: 1, signal: c.req.raw.signal }); return createAdminEvent(fn(prev), c); } -function updateUser(pubkey: string, n: Record, c: AppContext): Promise { +function updateUser(pubkey: string, n: Record, c: Context): Promise { return updateNames(30382, pubkey, n, c); } -function updateEventInfo(id: string, n: Record, c: AppContext): Promise { +function updateEventInfo(id: string, n: Record, c: Context): Promise { return updateNames(30383, id, n, c); } -async function updateNames(k: number, d: string, n: Record, c: AppContext): Promise { +async function updateNames(k: number, d: string, n: Record, c: Context): Promise { const signer = new AdminSigner(); const admin = await signer.getPublicKey(); @@ -158,7 +157,7 @@ async function updateNames(k: number, d: string, n: Record, c: } /** Push the event through the pipeline, rethrowing any RelayError. */ -async function publishEvent(event: NostrEvent, c: AppContext): Promise { +async function publishEvent(event: NostrEvent, c: Context): Promise { debug('EVENT', event); try { await pipeline.handleEvent(event, c.req.raw.signal); @@ -209,7 +208,7 @@ type Entity = { id: string }; type HeaderRecord = Record; /** Return results with pagination headers. Assumes chronological sorting of events. */ -function paginated(c: AppContext, events: NostrEvent[], entities: (Entity | undefined)[], headers: HeaderRecord = {}) { +function paginated(c: Context, events: NostrEvent[], entities: (Entity | undefined)[], headers: HeaderRecord = {}) { const link = buildLinkHeader(c.req.url, events); if (link) { @@ -240,7 +239,7 @@ function buildListLinkHeader(url: string, params: { offset: number; limit: numbe /** paginate a list of tags. */ function paginatedList( - c: AppContext, + c: Context, params: { offset: number; limit: number }, entities: unknown[], headers: HeaderRecord = {}, diff --git a/src/utils/upload.ts b/src/utils/upload.ts index 81c88c86..a2240c2b 100644 --- a/src/utils/upload.ts +++ b/src/utils/upload.ts @@ -1,6 +1,7 @@ +import { type Context } from '@hono/hono'; import { HTTPException } from '@hono/hono/http-exception'; +import { NUploader } from '@nostrify/nostrify'; -import { AppContext } from '@/app.ts'; import { Conf } from '@/config.ts'; import { DittoUpload, dittoUploads } from '@/DittoUploads.ts'; @@ -11,7 +12,7 @@ interface FileMeta { /** Upload a file, track it in the database, and return the resulting media object. */ export async function uploadFile( - c: AppContext, + c: Context<{ Variables: { uploader?: NUploader } }>, file: File, meta: FileMeta, signal?: AbortSignal,