mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
Store the Push Subscription in the database
This commit is contained in:
parent
049d99af9b
commit
4561ec0d00
4 changed files with 56 additions and 20 deletions
|
|
@ -263,7 +263,7 @@ app.get('/api/v1/mutes', requireSigner, mutesController);
|
||||||
app.get('/api/v1/markers', requireProof(), markersController);
|
app.get('/api/v1/markers', requireProof(), markersController);
|
||||||
app.post('/api/v1/markers', requireProof(), updateMarkersController);
|
app.post('/api/v1/markers', requireProof(), updateMarkersController);
|
||||||
|
|
||||||
app.post('/api/v1/push/subscription', requireSigner, pushSubscribeController);
|
app.post('/api/v1/push/subscription', requireProof(), 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', reactionsController);
|
||||||
app.get('/api/v1/pleroma/statuses/:id{[0-9a-f]{64}}/reactions/:emoji', reactionsController);
|
app.get('/api/v1/pleroma/statuses/:id{[0-9a-f]{64}}/reactions/:emoji', reactionsController);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
|
import { nip19 } from 'nostr-tools';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { AppController } from '@/app.ts';
|
import { AppController } from '@/app.ts';
|
||||||
|
import { Storages } from '@/storages.ts';
|
||||||
import { parseBody } from '@/utils/api.ts';
|
import { parseBody } from '@/utils/api.ts';
|
||||||
|
import { getTokenHash } from '@/utils/auth.ts';
|
||||||
|
|
||||||
const pushSubscribeSchema = z.object({
|
const pushSubscribeSchema = z.object({
|
||||||
subscription: z.object({
|
subscription: z.object({
|
||||||
|
|
@ -10,30 +13,62 @@ const pushSubscribeSchema = z.object({
|
||||||
p256dh: z.string(),
|
p256dh: z.string(),
|
||||||
auth: 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(),
|
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
|
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(),
|
||||||
|
}).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const pushSubscribeController: AppController = async (c) => {
|
export const pushSubscribeController: AppController = async (c) => {
|
||||||
const data = pushSubscribeSchema.safeParse(await parseBody(c.req.raw));
|
const BEARER_REGEX = new RegExp(`^Bearer (${nip19.BECH32_REGEX.source})$`);
|
||||||
|
|
||||||
if (!data.success) {
|
const header = c.req.header('authorization');
|
||||||
return c.json({ error: 'Invalid request', schema: data.error }, 400);
|
const match = header?.match(BEARER_REGEX);
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
return c.json({ error: 'Unauthorized' }, 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [_, bech32] = match;
|
||||||
|
|
||||||
|
if (!bech32.startsWith('token1')) {
|
||||||
|
return c.json({ error: 'Unauthorized' }, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const kysely = await Storages.kysely();
|
||||||
|
const signer = c.get('signer')!;
|
||||||
|
|
||||||
|
const result = pushSubscribeSchema.safeParse(await parseBody(c.req.raw));
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
return c.json({ error: 'Invalid request', schema: result.error }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { subscription, data } = result.data;
|
||||||
|
|
||||||
|
await kysely
|
||||||
|
.insertInto('push_subscriptions')
|
||||||
|
.values({
|
||||||
|
pubkey: await signer.getPublicKey(),
|
||||||
|
token_hash: await getTokenHash(bech32 as `token1${string}`),
|
||||||
|
endpoint: subscription.endpoint,
|
||||||
|
p256dh: subscription.keys.p256dh,
|
||||||
|
auth: subscription.keys.auth,
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
.execute();
|
||||||
|
|
||||||
return c.json({});
|
return c.json({});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ interface EventZapRow {
|
||||||
interface PushSubscriptionRow {
|
interface PushSubscriptionRow {
|
||||||
id: Generated<bigint>;
|
id: Generated<bigint>;
|
||||||
pubkey: string;
|
pubkey: string;
|
||||||
|
token_hash: Uint8Array;
|
||||||
endpoint: string;
|
endpoint: string;
|
||||||
p256dh: string;
|
p256dh: string;
|
||||||
auth: string;
|
auth: string;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ export async function up(db: Kysely<any>): Promise<void> {
|
||||||
.createTable('push_subscriptions')
|
.createTable('push_subscriptions')
|
||||||
.addColumn('id', 'bigint', (c) => c.primaryKey().autoIncrement())
|
.addColumn('id', 'bigint', (c) => c.primaryKey().autoIncrement())
|
||||||
.addColumn('pubkey', 'char(64)', (c) => c.notNull())
|
.addColumn('pubkey', 'char(64)', (c) => c.notNull())
|
||||||
.addColumn('token', 'char(64)', (c) => c.notNull())
|
.addColumn('token_hash', 'bytea', (c) => c.references('auth_tokens.token_hash').onDelete('cascade').notNull())
|
||||||
.addColumn('endpoint', 'text', (c) => c.notNull())
|
.addColumn('endpoint', 'text', (c) => c.notNull())
|
||||||
.addColumn('p256dh', 'text', (c) => c.notNull())
|
.addColumn('p256dh', 'text', (c) => c.notNull())
|
||||||
.addColumn('auth', 'text', (c) => c.notNull())
|
.addColumn('auth', 'text', (c) => c.notNull())
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue