mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
Refactor accountsController into a separate route
This commit is contained in:
parent
260340c58e
commit
7d877b5a37
7 changed files with 151 additions and 211 deletions
42
src/app.ts
42
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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
35
src/controllers/api/favourites.ts
Normal file
35
src/controllers/api/favourites.ts
Normal file
|
|
@ -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);
|
||||
});
|
||||
|
|
@ -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' });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,7 +9,8 @@ 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) => {
|
||||
export const uploaderMiddleware: MiddlewareHandler<{ Variables: { signer?: NostrSigner; uploader?: NUploader } }> =
|
||||
async (c, next) => {
|
||||
const signer = c.get('signer');
|
||||
|
||||
switch (Conf.uploader) {
|
||||
|
|
@ -29,7 +31,10 @@ export const uploaderMiddleware: AppMiddleware = async (c, next) => {
|
|||
);
|
||||
break;
|
||||
case 'ipfs':
|
||||
c.set('uploader', new IPFSUploader({ baseUrl: Conf.mediaDomain, apiUrl: Conf.ipfs.apiUrl, fetch: fetchWorker }));
|
||||
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 }));
|
||||
|
|
@ -45,4 +50,4 @@ export const uploaderMiddleware: AppMiddleware = async (c, next) => {
|
|||
}
|
||||
|
||||
await next();
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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<EventTemplate, 'content' | 'created_at' | 'tags'>;
|
||||
|
||||
/** 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');
|
||||
|
||||
if (!signer) {
|
||||
|
|
@ -50,7 +49,7 @@ interface UpdateEventFilter extends NostrFilter {
|
|||
async function updateEvent<E extends EventStub>(
|
||||
filter: UpdateEventFilter,
|
||||
fn: (prev: NostrEvent) => E | Promise<E>,
|
||||
c: AppContext,
|
||||
c: Context,
|
||||
): Promise<NostrEvent> {
|
||||
const store = await Storages.db();
|
||||
|
||||
|
|
@ -72,7 +71,7 @@ async function updateEvent<E extends EventStub>(
|
|||
function updateListEvent(
|
||||
filter: UpdateEventFilter,
|
||||
fn: (tags: string[][]) => string[][],
|
||||
c: AppContext,
|
||||
c: Context,
|
||||
): Promise<NostrEvent> {
|
||||
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<NostrEvent> {
|
||||
async function createAdminEvent(t: EventStub, c: Context): Promise<NostrEvent> {
|
||||
const signer = new AdminSigner();
|
||||
|
||||
const event = await signer.signEvent({
|
||||
|
|
@ -99,7 +98,7 @@ async function createAdminEvent(t: EventStub, c: AppContext): Promise<NostrEvent
|
|||
function updateListAdminEvent(
|
||||
filter: UpdateEventFilter,
|
||||
fn: (tags: string[][]) => string[][],
|
||||
c: AppContext,
|
||||
c: Context,
|
||||
): Promise<NostrEvent> {
|
||||
return updateAdminEvent(filter, (prev) => ({
|
||||
kind: filter.kinds[0],
|
||||
|
|
@ -112,22 +111,22 @@ function updateListAdminEvent(
|
|||
async function updateAdminEvent<E extends EventStub>(
|
||||
filter: UpdateEventFilter,
|
||||
fn: (prev: NostrEvent | undefined) => E,
|
||||
c: AppContext,
|
||||
c: Context,
|
||||
): Promise<NostrEvent> {
|
||||
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<string, boolean>, c: AppContext): Promise<NostrEvent> {
|
||||
function updateUser(pubkey: string, n: Record<string, boolean>, c: Context): Promise<NostrEvent> {
|
||||
return updateNames(30382, pubkey, n, c);
|
||||
}
|
||||
|
||||
function updateEventInfo(id: string, n: Record<string, boolean>, c: AppContext): Promise<NostrEvent> {
|
||||
function updateEventInfo(id: string, n: Record<string, boolean>, c: Context): Promise<NostrEvent> {
|
||||
return updateNames(30383, id, n, c);
|
||||
}
|
||||
|
||||
async function updateNames(k: number, d: string, n: Record<string, boolean>, c: AppContext): Promise<NostrEvent> {
|
||||
async function updateNames(k: number, d: string, n: Record<string, boolean>, c: Context): Promise<NostrEvent> {
|
||||
const signer = new AdminSigner();
|
||||
const admin = await signer.getPublicKey();
|
||||
|
||||
|
|
@ -158,7 +157,7 @@ async function updateNames(k: number, d: string, n: Record<string, boolean>, c:
|
|||
}
|
||||
|
||||
/** Push the event through the pipeline, rethrowing any RelayError. */
|
||||
async function publishEvent(event: NostrEvent, c: AppContext): Promise<NostrEvent> {
|
||||
async function publishEvent(event: NostrEvent, c: Context): Promise<NostrEvent> {
|
||||
debug('EVENT', event);
|
||||
try {
|
||||
await pipeline.handleEvent(event, c.req.raw.signal);
|
||||
|
|
@ -209,7 +208,7 @@ type Entity = { id: string };
|
|||
type HeaderRecord = Record<string, string | string[]>;
|
||||
|
||||
/** 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 = {},
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue