mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
183 lines
4.9 KiB
TypeScript
183 lines
4.9 KiB
TypeScript
import { NostrFilter, NSchema as n } from '@nostrify/nostrify';
|
|
import { z } from 'zod';
|
|
|
|
import { type AppController } from '@/app.ts';
|
|
import { Conf } from '@/config.ts';
|
|
import { createEvent, paginated, parseBody, updateEventInfo } from '@/utils/api.ts';
|
|
import { hydrateEvents } from '@/storages/hydrate.ts';
|
|
import { renderAdminReport } from '@/views/mastodon/reports.ts';
|
|
import { renderReport } from '@/views/mastodon/reports.ts';
|
|
import { booleanParamSchema } from '@/schema.ts';
|
|
|
|
const reportSchema = z.object({
|
|
account_id: n.id(),
|
|
status_ids: n.id().array().default([]),
|
|
comment: z.string().max(1000).default(''),
|
|
category: z.string().default('other'),
|
|
// TODO: rules_ids[] is not implemented
|
|
});
|
|
|
|
/** https://docs.joinmastodon.org/methods/reports/#post */
|
|
const reportController: AppController = async (c) => {
|
|
const store = c.get('store');
|
|
const body = await parseBody(c.req.raw);
|
|
const result = reportSchema.safeParse(body);
|
|
|
|
if (!result.success) {
|
|
return c.json(result.error, 422);
|
|
}
|
|
|
|
const {
|
|
account_id,
|
|
status_ids,
|
|
comment,
|
|
category,
|
|
} = result.data;
|
|
|
|
const tags = [
|
|
['p', account_id, category],
|
|
['P', Conf.pubkey],
|
|
];
|
|
|
|
for (const status of status_ids) {
|
|
tags.push(['e', status, category]);
|
|
}
|
|
|
|
const event = await createEvent({
|
|
kind: 1984,
|
|
content: comment,
|
|
tags,
|
|
}, c);
|
|
|
|
await hydrateEvents({ events: [event], store });
|
|
return c.json(await renderReport(event));
|
|
};
|
|
|
|
const adminReportsSchema = z.object({
|
|
resolved: booleanParamSchema.optional(),
|
|
account_id: n.id().optional(),
|
|
target_account_id: n.id().optional(),
|
|
});
|
|
|
|
/** https://docs.joinmastodon.org/methods/admin/reports/#get */
|
|
const adminReportsController: AppController = async (c) => {
|
|
const store = c.get('store');
|
|
const viewerPubkey = await c.get('signer')?.getPublicKey();
|
|
|
|
const params = c.get('pagination');
|
|
const { resolved, account_id, target_account_id } = adminReportsSchema.parse(c.req.query());
|
|
|
|
const filter: NostrFilter = {
|
|
kinds: [30383],
|
|
authors: [Conf.pubkey],
|
|
'#k': ['1984'],
|
|
...params,
|
|
};
|
|
|
|
if (typeof resolved === 'boolean') {
|
|
filter['#n'] = [resolved ? 'closed' : 'open'];
|
|
}
|
|
if (account_id) {
|
|
filter['#p'] = [account_id];
|
|
}
|
|
if (target_account_id) {
|
|
filter['#P'] = [target_account_id];
|
|
}
|
|
|
|
const orig = await store.query([filter]);
|
|
const ids = new Set<string>();
|
|
|
|
for (const event of orig) {
|
|
const d = event.tags.find(([name]) => name === 'd')?.[1];
|
|
if (d) {
|
|
ids.add(d);
|
|
}
|
|
}
|
|
|
|
const events = await store.query([{ kinds: [1984], ids: [...ids] }])
|
|
.then((events) => hydrateEvents({ store, events: events, signal: c.req.raw.signal }));
|
|
|
|
const reports = await Promise.all(
|
|
events.map((event) => renderAdminReport(event, { viewerPubkey })),
|
|
);
|
|
|
|
return paginated(c, orig, reports);
|
|
};
|
|
|
|
/** https://docs.joinmastodon.org/methods/admin/reports/#get-one */
|
|
const adminReportController: AppController = async (c) => {
|
|
const eventId = c.req.param('id');
|
|
const { signal } = c.req.raw;
|
|
const store = c.get('store');
|
|
const pubkey = await c.get('signer')?.getPublicKey();
|
|
|
|
const [event] = await store.query([{
|
|
kinds: [1984],
|
|
ids: [eventId],
|
|
limit: 1,
|
|
}], { signal });
|
|
|
|
if (!event) {
|
|
return c.json({ error: 'Not found' }, 404);
|
|
}
|
|
|
|
await hydrateEvents({ events: [event], store, signal });
|
|
|
|
const report = await renderAdminReport(event, { viewerPubkey: pubkey });
|
|
return c.json(report);
|
|
};
|
|
|
|
/** https://docs.joinmastodon.org/methods/admin/reports/#resolve */
|
|
const adminReportResolveController: AppController = async (c) => {
|
|
const eventId = c.req.param('id');
|
|
const { signal } = c.req.raw;
|
|
const store = c.get('store');
|
|
const pubkey = await c.get('signer')?.getPublicKey();
|
|
|
|
const [event] = await store.query([{
|
|
kinds: [1984],
|
|
ids: [eventId],
|
|
limit: 1,
|
|
}], { signal });
|
|
|
|
if (!event) {
|
|
return c.json({ error: 'Not found' }, 404);
|
|
}
|
|
|
|
await updateEventInfo(eventId, { open: false, closed: true }, c);
|
|
await hydrateEvents({ events: [event], store, signal });
|
|
|
|
const report = await renderAdminReport(event, { viewerPubkey: pubkey });
|
|
return c.json(report);
|
|
};
|
|
|
|
const adminReportReopenController: AppController = async (c) => {
|
|
const eventId = c.req.param('id');
|
|
const { signal } = c.req.raw;
|
|
const store = c.get('store');
|
|
const pubkey = await c.get('signer')?.getPublicKey();
|
|
|
|
const [event] = await store.query([{
|
|
kinds: [1984],
|
|
ids: [eventId],
|
|
limit: 1,
|
|
}], { signal });
|
|
|
|
if (!event) {
|
|
return c.json({ error: 'Not found' }, 404);
|
|
}
|
|
|
|
await updateEventInfo(eventId, { open: true, closed: false }, c);
|
|
await hydrateEvents({ events: [event], store, signal });
|
|
|
|
const report = await renderAdminReport(event, { viewerPubkey: pubkey });
|
|
return c.json(report);
|
|
};
|
|
|
|
export {
|
|
adminReportController,
|
|
adminReportReopenController,
|
|
adminReportResolveController,
|
|
adminReportsController,
|
|
reportController,
|
|
};
|