diff --git a/src/app.ts b/src/app.ts index 66c33424..4fd1a52a 100644 --- a/src/app.ts +++ b/src/app.ts @@ -40,6 +40,7 @@ import { adminRelaysController, adminSetRelaysController, deleteZapSplitsController, + getZapSplitsController, nameRequestController, nameRequestsController, updateZapSplitsController, @@ -263,6 +264,8 @@ app.put('/api/v1/admin/ditto/relays', requireRole('admin'), adminSetRelaysContro app.post('/api/v1/ditto/names', requireSigner, nameRequestController); app.get('/api/v1/ditto/names', requireSigner, nameRequestsController); +app.get('/api/v1/ditto/zap_splits', getZapSplitsController); + app.put('/api/v1/admin/ditto/zap_splits', requireRole('admin'), updateZapSplitsController); app.delete('/api/v1/admin/ditto/zap_splits', requireRole('admin'), deleteZapSplitsController); diff --git a/src/controllers/api/ditto.ts b/src/controllers/api/ditto.ts index d1ba002b..ed1f500d 100644 --- a/src/controllers/api/ditto.ts +++ b/src/controllers/api/ditto.ts @@ -1,18 +1,21 @@ import { NostrEvent, NostrFilter, NSchema as n } from '@nostrify/nostrify'; import { z } from 'zod'; +import { accountFromPubkey } from '@/views/mastodon/accounts.ts'; import { AppController } from '@/app.ts'; +import { addTag } from '@/utils/tags.ts'; import { AdminSigner } from '@/signers/AdminSigner.ts'; import { booleanParamSchema } from '@/schema.ts'; import { Conf } from '@/config.ts'; -import { Storages } from '@/storages.ts'; -import { hydrateEvents } from '@/storages/hydrate.ts'; import { createEvent, paginated, paginationSchema, parseBody } from '@/utils/api.ts'; -import { renderNameRequest } from '@/views/ditto.ts'; -import { getZapSplits } from '@/utils/zap-split.ts'; -import { updateListAdminEvent } from '@/utils/api.ts'; -import { addTag } from '@/utils/tags.ts'; import { deleteTag } from '@/utils/tags.ts'; +import { DittoZapSplits, getZapSplits } from '@/utils/zap-split.ts'; +import { getAuthor } from '@/queries.ts'; +import { hydrateEvents } from '@/storages/hydrate.ts'; +import { renderNameRequest } from '@/views/ditto.ts'; +import { renderAccount } from '@/views/mastodon/accounts.ts'; +import { Storages } from '@/storages.ts'; +import { updateListAdminEvent } from '@/utils/api.ts'; const markerSchema = z.enum(['read', 'write']); @@ -156,7 +159,7 @@ export const nameRequestsController: AppController = async (c) => { const zapSplitSchema = z.record( n.id(), z.object({ - amount: z.number().int().min(1).max(100), + weight: z.number().int().min(1).max(100), message: z.string().max(500), }), ); @@ -170,8 +173,8 @@ export const updateZapSplitsController: AppController = async (c) => { return c.json({ error: result.error }, 400); } - const zap_split = await getZapSplits(store, Conf.pubkey); - if (!zap_split) { + const dittoZapSplit = await getZapSplits(store, Conf.pubkey); + if (!dittoZapSplit) { return c.json({ error: 'Zap split not activated, restart the server.' }, 404); } @@ -186,7 +189,7 @@ export const updateZapSplitsController: AppController = async (c) => { { kinds: [30078], authors: [Conf.pubkey], '#d': ['pub.ditto.zapSplits'], limit: 1 }, (tags) => pubkeys.reduce((accumulator, pubkey) => { - return addTag(accumulator, ['p', pubkey, data[pubkey].amount.toString(), data[pubkey].message]); + return addTag(accumulator, ['p', pubkey, data[pubkey].weight.toString(), data[pubkey].message]); }, tags), c, ); @@ -205,8 +208,8 @@ export const deleteZapSplitsController: AppController = async (c) => { return c.json({ error: result.error }, 400); } - const zap_split = await getZapSplits(store, Conf.pubkey); - if (!zap_split) { + const dittoZapSplit = await getZapSplits(store, Conf.pubkey); + if (!dittoZapSplit) { return c.json({ error: 'Zap split not activated, restart the server.' }, 404); } @@ -223,3 +226,28 @@ export const deleteZapSplitsController: AppController = async (c) => { return c.json(200); }; + +export const getZapSplitsController: AppController = async (c) => { + const store = c.get('store'); + + const dittoZapSplit: DittoZapSplits | undefined = await getZapSplits(store, Conf.pubkey) ?? {}; + if (!dittoZapSplit) { + return c.json({ error: 'Zap split not activated, restart the server.' }, 404); + } + + const pubkeys = Object.keys(dittoZapSplit); + + const zapSplits = await Promise.all(pubkeys.map(async (pubkey) => { + const author = await getAuthor(pubkey); + + const account = author ? await renderAccount(author) : await accountFromPubkey(pubkey); + + return { + account, + weight: dittoZapSplit[pubkey].weight, + message: dittoZapSplit[pubkey].message, + }; + })); + + return c.json(zapSplits, 200); +}; diff --git a/src/controllers/api/instance.ts b/src/controllers/api/instance.ts index 5f7054ee..faca9c9f 100644 --- a/src/controllers/api/instance.ts +++ b/src/controllers/api/instance.ts @@ -4,16 +4,12 @@ import { AppController } from '@/app.ts'; import { Conf } from '@/config.ts'; import { Storages } from '@/storages.ts'; import { getInstanceMetadata } from '@/utils/instance.ts'; -import { DittoZapSplits, getZapSplits } from '@/utils/zap-split.ts'; const version = `3.0.0 (compatible; Ditto ${denoJson.version})`; const instanceV1Controller: AppController = async (c) => { const { host, protocol } = Conf.url; const meta = await getInstanceMetadata(await Storages.db(), c.req.raw.signal); - const store = c.get('store'); - - const zap_split: DittoZapSplits | undefined = await getZapSplits(store, Conf.pubkey) ?? {}; /** Protocol to use for WebSocket URLs, depending on the protocol of the `LOCAL_DOMAIN`. */ const wsProtocol = protocol === 'http:' ? 'ws:' : 'wss:'; @@ -72,9 +68,6 @@ const instanceV1Controller: AppController = async (c) => { }, }, rules: [], - ditto: { - zap_split, - }, }); }; diff --git a/src/controllers/api/statuses.ts b/src/controllers/api/statuses.ts index 4e8d051f..16c4c8bf 100644 --- a/src/controllers/api/statuses.ts +++ b/src/controllers/api/statuses.ts @@ -179,12 +179,12 @@ const createStatusController: AppController = async (c) => { const meta = n.json().pipe(n.metadata()).catch({}).parse(author?.content); const lnurl = getLnurl(meta); - const zap_split = await getZapSplits(store, Conf.pubkey); - if (lnurl && zap_split) { + const dittoZapSplit = await getZapSplits(store, Conf.pubkey); + if (lnurl && dittoZapSplit) { let totalSplit = 0; - for (const pubkey in zap_split) { - totalSplit += zap_split[pubkey].amount; - tags.push(['zap', pubkey, Conf.relay, zap_split[pubkey].amount.toString()]); + for (const pubkey in dittoZapSplit) { + totalSplit += dittoZapSplit[pubkey].weight; + tags.push(['zap', pubkey, Conf.relay, dittoZapSplit[pubkey].weight.toString()]); } if (totalSplit) { tags.push(['zap', author?.pubkey as string, Conf.relay, Math.max(0, 100 - totalSplit).toString()]); diff --git a/src/utils/zap-split.ts b/src/utils/zap-split.ts index 80d6cb2e..e5df1538 100644 --- a/src/utils/zap-split.ts +++ b/src/utils/zap-split.ts @@ -10,7 +10,7 @@ type ExtraMessage = string; type splitPercentages = number; export type DittoZapSplits = { - [key: Pubkey]: { amount: splitPercentages; message: ExtraMessage }; + [key: Pubkey]: { weight: splitPercentages; message: ExtraMessage }; }; /** Gets zap splits from NIP-78 in DittoZapSplits format. */ @@ -30,7 +30,7 @@ export async function getZapSplits(store: NStore, pubkey: string): Promise