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 { blocksController } from '@/controllers/api/blocks.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 { instanceController } from '@/controllers/api/instance.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.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/reports', requireSigner, reportController);

View File

@ -3,9 +3,11 @@ import { z } from 'zod';
import { AppController } from '@/app.ts';
import { Conf } from '@/config.ts';
import { Storages } from '@/storages.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 { renderNameRequest } from '@/views/ditto.ts';
const markerSchema = z.enum(['read', 'write']);
@ -60,15 +62,15 @@ function renderRelays(event: NostrEvent): RelayEntity[] {
}, [] as RelayEntity[]);
}
const inviteRequestSchema = z.object({
const nameRequestSchema = z.object({
nip05: z.string().email(),
reason: z.string().max(500).optional(),
});
export const inviteRequestController: AppController = async (c) => {
const { nip05, reason } = inviteRequestSchema.parse(await c.req.json());
export const nameRequestController: AppController = async (c) => {
const { nip05, reason } = nameRequestSchema.parse(await c.req.json());
await createEvent({
const event = await createEvent({
kind: 3036,
content: reason,
tags: [
@ -79,5 +81,20 @@ export const inviteRequestController: AppController = async (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
*/
reported_notes?: DittoEvent[];
/** Admin event relationship. */
info?: DittoEvent;
}

View File

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

View File

@ -44,6 +44,10 @@ async function hydrateEvents(opts: HydrateOpts): Promise<DittoEvent[]> {
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 })) {
cache.push(event);
}
@ -83,6 +87,7 @@ export function assembleEvents(
for (const event of a) {
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.info = b.find((e) => matchFilter({ kinds: [30383], authors: [admin], '#d': [event.id] }, e));
if (event.kind === 1) {
const id = findQuoteTag(event.tags)?.[1] || findQuoteInContent(event.content);
@ -106,21 +111,22 @@ export function assembleEvents(
}
if (event.kind === 1984) {
const targetAccountId = event.tags.find(([name]) => name === 'p')?.[1];
if (targetAccountId) {
event.reported_profile = b.find((e) => matchFilter({ kinds: [0], authors: [targetAccountId] }, e));
const pubkey = event.tags.find(([name]) => name === 'p')?.[1];
if (pubkey) {
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]);
if (status_ids.length > 0) {
for (const id of status_ids) {
const reportedEvent = b.find((e) => matchFilter({ kinds: [1], ids: [id] }, e));
if (reportedEvent) reportedEvents.push(reportedEvent);
const reportedEvents: DittoEvent[] = [];
const ids = event.tags.filter(([name]) => name === 'e').map(([_name, value]) => value);
for (const id of ids) {
const reported = b.find((e) => matchFilter({ kinds: [1], ids: [id] }, e));
if (reported) {
reportedEvents.push(reported);
}
}
event.reported_notes = reportedEvents;
}
}
event.author_stats = stats.authors.find((stats) => stats.pubkey === event.pubkey);
event.event_stats = eventStats.find((stats) => stats.event_id === event.id);
@ -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. */
function gatherReportedNotes({ events, store, signal }: HydrateOpts): Promise<DittoEvent[]> {
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(),
};
}