diff --git a/src/app.ts b/src/app.ts index 9fb67ced..edae5283 100644 --- a/src/app.ts +++ b/src/app.ts @@ -68,6 +68,7 @@ import { updateConfigController, } from '@/controllers/api/pleroma.ts'; import { preferencesController } from '@/controllers/api/preferences.ts'; +import { pushSubscribeController } from '@/controllers/api/push.ts'; import { deleteReactionController, reactionController, reactionsController } from '@/controllers/api/reactions.ts'; import { relayController } from '@/controllers/nostr/relay.ts'; import { @@ -262,6 +263,8 @@ app.get('/api/v1/mutes', requireSigner, mutesController); app.get('/api/v1/markers', requireProof(), markersController); app.post('/api/v1/markers', requireProof(), updateMarkersController); +app.post('/api/v1/push/subscription', requireSigner, pushSubscribeController); + app.get('/api/v1/pleroma/statuses/:id{[0-9a-f]{64}}/reactions', reactionsController); app.get('/api/v1/pleroma/statuses/:id{[0-9a-f]{64}}/reactions/:emoji', reactionsController); app.put('/api/v1/pleroma/statuses/:id{[0-9a-f]{64}}/reactions/:emoji', requireSigner, reactionController); diff --git a/src/controllers/api/push.ts b/src/controllers/api/push.ts new file mode 100644 index 00000000..987af708 --- /dev/null +++ b/src/controllers/api/push.ts @@ -0,0 +1,39 @@ +import { z } from 'zod'; + +import { AppController } from '@/app.ts'; +import { parseBody } from '@/utils/api.ts'; + +const pushSubscribeSchema = z.object({ + subscription: z.object({ + endpoint: z.string(), + keys: z.object({ + p256dh: z.string(), + auth: z.string(), + }), + data: z.object({ + alerts: z.object({ + mention: z.boolean().optional(), + status: z.boolean().optional(), + reblog: z.boolean().optional(), + follow: z.boolean().optional(), + follow_request: z.boolean().optional(), + favourite: z.boolean().optional(), + poll: z.boolean().optional(), + update: z.boolean().optional(), + 'admin.sign_up': z.boolean().optional(), + 'admin.report': z.boolean().optional(), + }).optional(), + policy: z.enum(['all', 'followed', 'follower', 'none']).optional(), + }), + }), +}); + +export const pushSubscribeController: AppController = async (c) => { + const data = pushSubscribeSchema.safeParse(await parseBody(c.req.raw)); + + if (!data.success) { + return c.json({ error: 'Invalid request', schema: data.error }, 400); + } + + return c.json({}); +}; diff --git a/src/db/DittoTables.ts b/src/db/DittoTables.ts index c05ffe66..26b2e6fa 100644 --- a/src/db/DittoTables.ts +++ b/src/db/DittoTables.ts @@ -9,6 +9,7 @@ export interface DittoTables extends NPostgresSchema { event_stats: EventStatsRow; pubkey_domains: PubkeyDomainRow; event_zaps: EventZapRow; + push_subscriptions: PushSubscriptionRow; } type NostrEventsRow = NPostgresSchema['nostr_events'] & { @@ -55,3 +56,28 @@ interface EventZapRow { amount_millisats: number; comment: string; } + +interface PushSubscriptionRow { + id: bigint; + pubkey: string; + endpoint: string; + key_p256dh: string; + key_auth: string; + data: { + alerts?: { + mention?: boolean; + status?: boolean; + reblog?: boolean; + follow?: boolean; + follow_request?: boolean; + favourite?: boolean; + poll?: boolean; + update?: boolean; + 'admin.sign_up'?: boolean; + 'admin.report'?: boolean; + }; + policy?: 'all' | 'followed' | 'follower' | 'none'; + } | null; + created_at: Date; + updated_at: Date; +} diff --git a/src/types/MastodonPush.ts b/src/types/MastodonPush.ts new file mode 100644 index 00000000..dc2edbfa --- /dev/null +++ b/src/types/MastodonPush.ts @@ -0,0 +1,15 @@ +/** + * Mastodon push payload. + * + * This is the object the server sends to the client (with the Web Push API) + * to notify of a new push event. + */ +export interface MastodonPush { + access_token: string; + preferred_locale: string; + notification_id: string; + notification_type: string; + icon: string; + title: string; + body: string; +}