Add followers/following endpoints
Fixes https://gitlab.com/soapbox-pub/ditto/-/issues/69
This commit is contained in:
parent
728f0bd657
commit
9b67d7a7c0
|
@ -18,6 +18,8 @@ import {
|
|||
accountStatusesController,
|
||||
createAccountController,
|
||||
followController,
|
||||
followersController,
|
||||
followingController,
|
||||
relationshipsController,
|
||||
updateCredentialsController,
|
||||
verifyCredentialsController,
|
||||
|
@ -105,6 +107,8 @@ app.get('/api/v1/accounts/search', accountSearchController);
|
|||
app.get('/api/v1/accounts/lookup', accountLookupController);
|
||||
app.get('/api/v1/accounts/relationships', relationshipsController);
|
||||
app.post('/api/v1/accounts/:pubkey{[0-9a-f]{64}}/follow', followController);
|
||||
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);
|
||||
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { type AppController } from '@/app.ts';
|
||||
import { type Filter, findReplyTag, z } from '@/deps.ts';
|
||||
import * as mixer from '@/mixer.ts';
|
||||
import { getAuthor, getFollows, syncUser } from '@/queries.ts';
|
||||
import { getAuthor, getFollowedPubkeys, getFollows, syncUser } from '@/queries.ts';
|
||||
import { booleanParamSchema } from '@/schema.ts';
|
||||
import { jsonMetaContentSchema } from '@/schemas/nostr.ts';
|
||||
import { toAccount, toRelationship, toStatus } from '@/transformers/nostr-to-mastoapi.ts';
|
||||
import { isFollowing, lookupAccount } from '@/utils.ts';
|
||||
import { paginated, paginationSchema, parseBody } from '@/utils/web.ts';
|
||||
import { createEvent } from '@/utils/web.ts';
|
||||
import { renderEventAccounts } from '@/views.ts';
|
||||
|
||||
const createAccountController: AppController = (c) => {
|
||||
return c.json({ error: 'Please log in with Nostr.' }, 405);
|
||||
|
@ -173,6 +174,25 @@ const followController: AppController = async (c) => {
|
|||
return c.json(relationship);
|
||||
};
|
||||
|
||||
const followersController: AppController = (c) => {
|
||||
const pubkey = c.req.param('pubkey');
|
||||
const params = paginationSchema.parse(c.req.query());
|
||||
return renderEventAccounts(c, [{ kinds: [3], '#p': [pubkey], ...params }]);
|
||||
};
|
||||
|
||||
const followingController: AppController = async (c) => {
|
||||
const pubkey = c.req.param('pubkey');
|
||||
const pubkeys = await getFollowedPubkeys(pubkey);
|
||||
|
||||
// TODO: pagination by offset.
|
||||
const accounts = await Promise.all(pubkeys.map(async (pubkey) => {
|
||||
const event = await getAuthor(pubkey);
|
||||
return event ? await toAccount(event) : undefined;
|
||||
}));
|
||||
|
||||
return c.json(accounts.filter(Boolean));
|
||||
};
|
||||
|
||||
export {
|
||||
accountController,
|
||||
accountLookupController,
|
||||
|
@ -180,6 +200,8 @@ export {
|
|||
accountStatusesController,
|
||||
createAccountController,
|
||||
followController,
|
||||
followersController,
|
||||
followingController,
|
||||
relationshipsController,
|
||||
updateCredentialsController,
|
||||
verifyCredentialsController,
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { type AppController } from '@/app.ts';
|
||||
import { type Event, ISO6391, z } from '@/deps.ts';
|
||||
import * as mixer from '@/mixer.ts';
|
||||
import { getAncestors, getAuthor, getDescendants, getEvent } from '@/queries.ts';
|
||||
import { toAccount, toStatus } from '@/transformers/nostr-to-mastoapi.ts';
|
||||
import { createEvent, paginated, parseBody } from '@/utils/web.ts';
|
||||
import { getAncestors, getDescendants, getEvent } from '@/queries.ts';
|
||||
import { toStatus } from '@/transformers/nostr-to-mastoapi.ts';
|
||||
import { createEvent, paginationSchema, parseBody } from '@/utils/web.ts';
|
||||
import { renderEventAccounts } from '@/views.ts';
|
||||
|
||||
const createStatusSchema = z.object({
|
||||
in_reply_to_id: z.string().regex(/[0-9a-f]{64}/).nullish(),
|
||||
|
@ -129,34 +129,16 @@ const favouriteController: AppController = async (c) => {
|
|||
}
|
||||
};
|
||||
|
||||
const favouritedByController: AppController = async (c) => {
|
||||
const favouritedByController: AppController = (c) => {
|
||||
const id = c.req.param('id');
|
||||
|
||||
const events = await mixer.getFilters([{ kinds: [7], '#e': [id] }]);
|
||||
|
||||
const accounts = await Promise.all(events.map(async ({ pubkey }) => {
|
||||
const author = await getAuthor(pubkey);
|
||||
if (author) {
|
||||
return toAccount(author);
|
||||
}
|
||||
}));
|
||||
|
||||
return paginated(c, events, accounts);
|
||||
const params = paginationSchema.parse(c.req.query());
|
||||
return renderEventAccounts(c, [{ kinds: [7], '#e': [id], ...params }]);
|
||||
};
|
||||
|
||||
const rebloggedByController: AppController = async (c) => {
|
||||
const rebloggedByController: AppController = (c) => {
|
||||
const id = c.req.param('id');
|
||||
|
||||
const events = await mixer.getFilters([{ kinds: [6], '#e': [id] }]);
|
||||
|
||||
const accounts = await Promise.all(events.map(async ({ pubkey }) => {
|
||||
const author = await getAuthor(pubkey);
|
||||
if (author) {
|
||||
return toAccount(author);
|
||||
}
|
||||
}));
|
||||
|
||||
return paginated(c, events, accounts);
|
||||
const params = paginationSchema.parse(c.req.query());
|
||||
return renderEventAccounts(c, [{ kinds: [6], '#e': [id], ...params }]);
|
||||
};
|
||||
|
||||
export {
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import { AppContext } from '@/app.ts';
|
||||
import { type Filter } from '@/deps.ts';
|
||||
import * as mixer from '@/mixer.ts';
|
||||
import { getAuthor } from '@/queries.ts';
|
||||
import { toAccount } from '@/transformers/nostr-to-mastoapi.ts';
|
||||
import { paginated } from '@/utils/web.ts';
|
||||
|
||||
/** Render account objects for the author of each event. */
|
||||
async function renderEventAccounts(c: AppContext, filters: Filter[]) {
|
||||
const events = await mixer.getFilters(filters);
|
||||
const pubkeys = new Set(events.map(({ pubkey }) => pubkey));
|
||||
|
||||
if (!pubkeys.size) {
|
||||
return c.json([]);
|
||||
}
|
||||
|
||||
const accounts = await Promise.all([...pubkeys].map(async (pubkey) => {
|
||||
const author = await getAuthor(pubkey);
|
||||
if (author) {
|
||||
return toAccount(author);
|
||||
}
|
||||
}));
|
||||
|
||||
return paginated(c, events, accounts);
|
||||
}
|
||||
|
||||
export { renderEventAccounts };
|
Loading…
Reference in New Issue