Add preliminary nameRequestsController

This commit is contained in:
Alex Gleason 2024-06-08 22:16:34 -05:00
parent a14515bbe0
commit bd6424acf5
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7
6 changed files with 89 additions and 19 deletions

View File

@ -30,7 +30,7 @@ import { adminAccountsController, adminActionController } from '@/controllers/ap
import { appCredentialsController, createAppController } from '@/controllers/api/apps.ts'; import { appCredentialsController, createAppController } from '@/controllers/api/apps.ts';
import { blocksController } from '@/controllers/api/blocks.ts'; import { blocksController } from '@/controllers/api/blocks.ts';
import { bookmarksController } from '@/controllers/api/bookmarks.ts'; import { bookmarksController } from '@/controllers/api/bookmarks.ts';
import { adminRelaysController, adminSetRelaysController, inviteRequestController } from '@/controllers/api/ditto.ts'; import { adminRelaysController, adminSetRelaysController, nameRequestController } from '@/controllers/api/ditto.ts';
import { emptyArrayController, emptyObjectController, notImplementedController } from '@/controllers/api/fallback.ts'; import { emptyArrayController, emptyObjectController, notImplementedController } from '@/controllers/api/fallback.ts';
import { instanceController } from '@/controllers/api/instance.ts'; import { instanceController } from '@/controllers/api/instance.ts';
import { markersController, updateMarkersController } from '@/controllers/api/markers.ts'; import { markersController, updateMarkersController } from '@/controllers/api/markers.ts';
@ -243,7 +243,7 @@ app.delete('/api/v1/pleroma/admin/statuses/:id', requireRole('admin'), pleromaAd
app.get('/api/v1/admin/ditto/relays', requireRole('admin'), adminRelaysController); app.get('/api/v1/admin/ditto/relays', requireRole('admin'), adminRelaysController);
app.put('/api/v1/admin/ditto/relays', requireRole('admin'), adminSetRelaysController); app.put('/api/v1/admin/ditto/relays', requireRole('admin'), adminSetRelaysController);
app.post('/api/v1/ditto/nip05', requireSigner, inviteRequestController); app.post('/api/v1/ditto/names', requireSigner, nameRequestController);
app.post('/api/v1/ditto/zap', requireSigner, zapController); app.post('/api/v1/ditto/zap', requireSigner, zapController);
app.post('/api/v1/reports', requireSigner, reportController); app.post('/api/v1/reports', requireSigner, reportController);

View File

@ -3,9 +3,11 @@ import { z } from 'zod';
import { AppController } from '@/app.ts'; import { AppController } from '@/app.ts';
import { Conf } from '@/config.ts'; import { Conf } from '@/config.ts';
import { Storages } from '@/storages.ts';
import { AdminSigner } from '@/signers/AdminSigner.ts'; import { AdminSigner } from '@/signers/AdminSigner.ts';
import { Storages } from '@/storages.ts';
import { hydrateEvents } from '@/storages/hydrate.ts';
import { createEvent } from '@/utils/api.ts'; import { createEvent } from '@/utils/api.ts';
import { renderNameRequest } from '@/views/ditto.ts';
const markerSchema = z.enum(['read', 'write']); const markerSchema = z.enum(['read', 'write']);
@ -60,15 +62,15 @@ function renderRelays(event: NostrEvent): RelayEntity[] {
}, [] as RelayEntity[]); }, [] as RelayEntity[]);
} }
const inviteRequestSchema = z.object({ const nameRequestSchema = z.object({
nip05: z.string().email(), nip05: z.string().email(),
reason: z.string().max(500).optional(), reason: z.string().max(500).optional(),
}); });
export const inviteRequestController: AppController = async (c) => { export const nameRequestController: AppController = async (c) => {
const { nip05, reason } = inviteRequestSchema.parse(await c.req.json()); const { nip05, reason } = nameRequestSchema.parse(await c.req.json());
await createEvent({ const event = await createEvent({
kind: 3036, kind: 3036,
content: reason, content: reason,
tags: [ tags: [
@ -79,5 +81,20 @@ export const inviteRequestController: AppController = async (c) => {
], ],
}, c); }, c);
return new Response(null, { status: 204 }); await hydrateEvents({ events: [event], store: await Storages.db() });
const nameRequest = await renderNameRequest(event);
return c.json(nameRequest);
};
export const nameRequestsController: AppController = async (c) => {
const store = await Storages.db();
const signer = c.get('signer')!;
const pubkey = await signer.getPublicKey();
const events = await store.query([{ kinds: [3036], authors: [pubkey], limit: 20 }])
.then((events) => hydrateEvents({ events, store }));
const nameRequests = await Promise.all(events.map(renderNameRequest));
return c.json(nameRequests);
}; };

View File

@ -34,4 +34,6 @@ export interface DittoEvent extends NostrEvent {
* https://github.com/nostr-protocol/nips/blob/master/56.md * https://github.com/nostr-protocol/nips/blob/master/56.md
*/ */
reported_notes?: DittoEvent[]; reported_notes?: DittoEvent[];
/** Admin event relationship. */
info?: DittoEvent;
} }

View File

@ -212,7 +212,7 @@ async function generateSetEvents(event: NostrEvent): Promise<void> {
['d', event.id], ['d', event.id],
['p', event.pubkey], ['p', event.pubkey],
['k', '3036'], ['k', '3036'],
['n', 'open'], ['n', 'pending'],
], ],
created_at: Math.floor(Date.now() / 1000), created_at: Math.floor(Date.now() / 1000),
}); });

View File

@ -44,6 +44,10 @@ async function hydrateEvents(opts: HydrateOpts): Promise<DittoEvent[]> {
cache.push(event); cache.push(event);
} }
for (const event of await gatherInfo({ events: cache, store, signal })) {
cache.push(event);
}
for (const event of await gatherReportedProfiles({ events: cache, store, signal })) { for (const event of await gatherReportedProfiles({ events: cache, store, signal })) {
cache.push(event); cache.push(event);
} }
@ -83,6 +87,7 @@ export function assembleEvents(
for (const event of a) { for (const event of a) {
event.author = b.find((e) => matchFilter({ kinds: [0], authors: [event.pubkey] }, e)); event.author = b.find((e) => matchFilter({ kinds: [0], authors: [event.pubkey] }, e));
event.user = b.find((e) => matchFilter({ kinds: [30382], authors: [admin], '#d': [event.pubkey] }, e)); event.user = b.find((e) => matchFilter({ kinds: [30382], authors: [admin], '#d': [event.pubkey] }, e));
event.info = b.find((e) => matchFilter({ kinds: [30383], authors: [admin], '#d': [event.id] }, e));
if (event.kind === 1) { if (event.kind === 1) {
const id = findQuoteTag(event.tags)?.[1] || findQuoteInContent(event.content); const id = findQuoteTag(event.tags)?.[1] || findQuoteInContent(event.content);
@ -106,20 +111,21 @@ export function assembleEvents(
} }
if (event.kind === 1984) { if (event.kind === 1984) {
const targetAccountId = event.tags.find(([name]) => name === 'p')?.[1]; const pubkey = event.tags.find(([name]) => name === 'p')?.[1];
if (targetAccountId) { if (pubkey) {
event.reported_profile = b.find((e) => matchFilter({ kinds: [0], authors: [targetAccountId] }, e)); event.reported_profile = b.find((e) => matchFilter({ kinds: [0], authors: [pubkey] }, e));
} }
const reportedEvents: DittoEvent[] = [];
const status_ids = event.tags.filter(([name]) => name === 'e').map((tag) => tag[1]); const reportedEvents: DittoEvent[] = [];
if (status_ids.length > 0) { const ids = event.tags.filter(([name]) => name === 'e').map(([_name, value]) => value);
for (const id of status_ids) {
const reportedEvent = b.find((e) => matchFilter({ kinds: [1], ids: [id] }, e)); for (const id of ids) {
if (reportedEvent) reportedEvents.push(reportedEvent); const reported = b.find((e) => matchFilter({ kinds: [1], ids: [id] }, e));
if (reported) {
reportedEvents.push(reported);
} }
event.reported_notes = reportedEvents;
} }
event.reported_notes = reportedEvents;
} }
event.author_stats = stats.authors.find((stats) => stats.pubkey === event.pubkey); event.author_stats = stats.authors.find((stats) => stats.pubkey === event.pubkey);
@ -206,6 +212,26 @@ function gatherUsers({ events, store, signal }: HydrateOpts): Promise<DittoEvent
); );
} }
/** Collect info events from the events. */
function gatherInfo({ events, store, signal }: HydrateOpts): Promise<DittoEvent[]> {
const ids = new Set<string>();
for (const event of events) {
if (event.kind === 3036) {
ids.add(event.id);
}
}
if (!ids.size) {
return Promise.resolve([]);
}
return store.query(
[{ ids: [...ids], limit: ids.size }],
{ signal },
);
}
/** Collect reported notes from the events. */ /** Collect reported notes from the events. */
function gatherReportedNotes({ events, store, signal }: HydrateOpts): Promise<DittoEvent[]> { function gatherReportedNotes({ events, store, signal }: HydrateOpts): Promise<DittoEvent[]> {
const ids = new Set<string>(); const ids = new Set<string>();

25
src/views/ditto.ts Normal file
View File

@ -0,0 +1,25 @@
import { DittoEvent } from '@/interfaces/DittoEvent.ts';
import { accountFromPubkey, renderAccount } from '@/views/mastodon/accounts.ts';
import { getTagSet } from '@/utils/tags.ts';
export async function renderNameRequest(event: DittoEvent) {
const n = getTagSet(event.info?.tags ?? [], 'n');
let approvalStatus = 'pending';
if (n.has('approved')) {
approvalStatus = 'approved';
}
if (n.has('rejected')) {
approvalStatus = 'rejected';
}
return {
id: event.id,
account: event.author ? await renderAccount(event.author) : accountFromPubkey(event.pubkey),
name: event.tags.find(([name]) => name === 'r')?.[1] || '',
reason: event.content,
approval_status: approvalStatus,
created_at: new Date(event.created_at * 1000).toISOString(),
};
}