kopia lustrzana https://github.com/cloudflare/wildebeest
				
				
				
			subscription: remove hardcoded and switch to Int
							rodzic
							
								
									3d6f616785
								
							
						
					
					
						commit
						39046061db
					
				|  | @ -31,8 +31,23 @@ export interface CreateRequest { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export type Subscription = { | export type Subscription = { | ||||||
| 	id: string | 	// While the spec says to use a string as id (https://docs.joinmastodon.org/entities/WebPushSubscription/#id), Mastodon's android app decided to violate that (https://github.com/mastodon/mastodon-android/blob/master/mastodon/src/main/java/org/joinmastodon/android/model/PushSubscription.java#LL11).
 | ||||||
|  | 	id: number | ||||||
|  | 
 | ||||||
| 	gateway: PushSubscription | 	gateway: PushSubscription | ||||||
|  | 	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: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export async function createSubscription( | export async function createSubscription( | ||||||
|  | @ -41,39 +56,33 @@ export async function createSubscription( | ||||||
| 	client: Client, | 	client: Client, | ||||||
| 	req: CreateRequest | 	req: CreateRequest | ||||||
| ): Promise<Subscription> { | ): Promise<Subscription> { | ||||||
| 	const id = crypto.randomUUID() |  | ||||||
| 
 |  | ||||||
| 	const query = ` | 	const query = ` | ||||||
|           INSERT INTO subscriptions (id, actor_id, client_id, endpoint, key_p256dh, key_auth, alert_mention, alert_status, alert_reblog, alert_follow, alert_follow_request, alert_favourite, alert_poll, alert_update, alert_admin_sign_up, alert_admin_report, policy) |           INSERT INTO subscriptions (actor_id, client_id, endpoint, key_p256dh, key_auth, alert_mention, alert_status, alert_reblog, alert_follow, alert_follow_request, alert_favourite, alert_poll, alert_update, alert_admin_sign_up, alert_admin_report, policy) | ||||||
|           VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) |           VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) | ||||||
|  |           RETURNING * | ||||||
|     ` |     ` | ||||||
| 	const out = await db | 	const row = await db | ||||||
| 		.prepare(query) | 		.prepare(query) | ||||||
| 		.bind( | 		.bind( | ||||||
| 			id, |  | ||||||
| 			actor.id.toString(), | 			actor.id.toString(), | ||||||
| 			client.id, | 			client.id, | ||||||
| 			req.subscription.endpoint, | 			req.subscription.endpoint, | ||||||
| 			req.subscription.keys.p256dh, | 			req.subscription.keys.p256dh, | ||||||
| 			req.subscription.keys.auth, | 			req.subscription.keys.auth, | ||||||
| 			req.data.alerts.mention ? 1 : 0, | 			req.data.alerts.mention === false ? 0 : 1, | ||||||
| 			req.data.alerts.status ? 1 : 0, | 			req.data.alerts.status === false ? 0 : 1, | ||||||
| 			req.data.alerts.reblog ? 1 : 0, | 			req.data.alerts.reblog === false ? 0 : 1, | ||||||
| 			req.data.alerts.follow ? 1 : 0, | 			req.data.alerts.follow === false ? 0 : 1, | ||||||
| 			req.data.alerts.follow_request ? 1 : 0, | 			req.data.alerts.follow_request === false ? 0 : 1, | ||||||
| 			req.data.alerts.favourite ? 1 : 0, | 			req.data.alerts.favourite === false ? 0 : 1, | ||||||
| 			req.data.alerts.poll ? 1 : 0, | 			req.data.alerts.poll === false ? 0 : 1, | ||||||
| 			req.data.alerts.update ? 1 : 0, | 			req.data.alerts.update === false ? 0 : 1, | ||||||
| 			req.data.alerts.admin_sign_up ? 1 : 0, | 			req.data.alerts.admin_sign_up === false ? 0 : 1, | ||||||
| 			req.data.alerts.admin_report ? 1 : 0, | 			req.data.alerts.admin_report === false ? 0 : 1, | ||||||
| 			req.data.policy | 			req.data.policy | ||||||
| 		) | 		) | ||||||
| 		.run() | 		.first<any>() | ||||||
| 	if (!out.success) { | 	return subscriptionFromRow(row) | ||||||
| 		throw new Error('SQL error: ' + out.error) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return { id, gateway: req.subscription } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export async function getSubscription(db: D1Database, actor: Actor, client: Client): Promise<Subscription | null> { | export async function getSubscription(db: D1Database, actor: Actor, client: Client): Promise<Subscription | null> { | ||||||
|  | @ -121,6 +130,19 @@ function subscriptionFromRow(row: any): Subscription { | ||||||
| 				auth: row.key_auth, | 				auth: row.key_auth, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
|  | 		alerts: { | ||||||
|  | 			mention: row.alert_mention === 1, | ||||||
|  | 			status: row.alert_status === 1, | ||||||
|  | 			reblog: row.alert_reblog === 1, | ||||||
|  | 			follow: row.alert_follow === 1, | ||||||
|  | 			follow_request: row.alert_follow_request === 1, | ||||||
|  | 			favourite: row.alert_favourite === 1, | ||||||
|  | 			poll: row.alert_poll === 1, | ||||||
|  | 			update: row.alert_update === 1, | ||||||
|  | 			admin_sign_up: row.alert_admin_sign_up === 1, | ||||||
|  | 			admin_report: row.alert_admin_report === 1, | ||||||
|  | 		}, | ||||||
|  | 		policy: row.policy, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,5 +1,4 @@ | ||||||
| import { strict as assert } from 'node:assert/strict' | import { strict as assert } from 'node:assert/strict' | ||||||
| import type { JWK } from 'wildebeest/backend/src/webpush/jwk' |  | ||||||
| import type { Env } from 'wildebeest/backend/src/types/env' | import type { Env } from 'wildebeest/backend/src/types/env' | ||||||
| import * as v1_instance from 'wildebeest/functions/api/v1/instance' | import * as v1_instance from 'wildebeest/functions/api/v1/instance' | ||||||
| import * as v2_instance from 'wildebeest/functions/api/v2/instance' | import * as v2_instance from 'wildebeest/functions/api/v2/instance' | ||||||
|  | @ -7,24 +6,11 @@ import * as apps from 'wildebeest/functions/api/v1/apps' | ||||||
| import * as custom_emojis from 'wildebeest/functions/api/v1/custom_emojis' | import * as custom_emojis from 'wildebeest/functions/api/v1/custom_emojis' | ||||||
| import * as mutes from 'wildebeest/functions/api/v1/mutes' | import * as mutes from 'wildebeest/functions/api/v1/mutes' | ||||||
| import * as blocks from 'wildebeest/functions/api/v1/blocks' | import * as blocks from 'wildebeest/functions/api/v1/blocks' | ||||||
| import { makeDB, assertCORS, assertJSON, assertCache, createTestClient } from './utils' | import { makeDB, assertCORS, assertJSON, assertCache, generateVAPIDKeys } from './utils' | ||||||
| import { createPerson } from 'wildebeest/backend/src/activitypub/actors' |  | ||||||
| import { createSubscription } from '../src/mastodon/subscription' |  | ||||||
| import * as subscription from 'wildebeest/functions/api/v1/push/subscription' |  | ||||||
| import { enrichStatus } from 'wildebeest/backend/src/mastodon/microformats' | import { enrichStatus } from 'wildebeest/backend/src/mastodon/microformats' | ||||||
| 
 | 
 | ||||||
| const userKEK = 'test_kek' |  | ||||||
| const domain = 'cloudflare.com' | const domain = 'cloudflare.com' | ||||||
| 
 | 
 | ||||||
| async function generateVAPIDKeys(): Promise<JWK> { |  | ||||||
| 	const keyPair = (await crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, [ |  | ||||||
| 		'sign', |  | ||||||
| 		'verify', |  | ||||||
| 	])) as CryptoKeyPair |  | ||||||
| 	const jwk = (await crypto.subtle.exportKey('jwk', keyPair.privateKey)) as JWK |  | ||||||
| 	return jwk |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| describe('Mastodon APIs', () => { | describe('Mastodon APIs', () => { | ||||||
| 	describe('instance', () => { | 	describe('instance', () => { | ||||||
| 		type Data = { | 		type Data = { | ||||||
|  | @ -158,113 +144,6 @@ describe('Mastodon APIs', () => { | ||||||
| 		}) | 		}) | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	describe('subscriptions', () => { |  | ||||||
| 		test('get non existing subscription', async () => { |  | ||||||
| 			const db = await makeDB() |  | ||||||
| 			const req = new Request('https://example.com') |  | ||||||
| 			const client = await createTestClient(db) |  | ||||||
| 			const connectedActor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com') |  | ||||||
| 
 |  | ||||||
| 			const res = await subscription.handleGetRequest(db, req, connectedActor, client.id) |  | ||||||
| 			assert.equal(res.status, 404) |  | ||||||
| 		}) |  | ||||||
| 
 |  | ||||||
| 		test('get existing subscription', async () => { |  | ||||||
| 			const db = await makeDB() |  | ||||||
| 			const req = new Request('https://example.com') |  | ||||||
| 			const client = await createTestClient(db) |  | ||||||
| 			const connectedActor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com') |  | ||||||
| 
 |  | ||||||
| 			const data: any = { |  | ||||||
| 				subscription: { |  | ||||||
| 					endpoint: 'https://endpoint.com', |  | ||||||
| 					keys: { |  | ||||||
| 						p256dh: 'p256dh', |  | ||||||
| 						auth: 'auth', |  | ||||||
| 					}, |  | ||||||
| 				}, |  | ||||||
| 				data: { |  | ||||||
| 					alerts: {}, |  | ||||||
| 					policy: 'all', |  | ||||||
| 				}, |  | ||||||
| 			} |  | ||||||
| 			await createSubscription(db, connectedActor, client, data) |  | ||||||
| 
 |  | ||||||
| 			const res = await subscription.handleGetRequest(db, req, connectedActor, client.id) |  | ||||||
| 			assert.equal(res.status, 200) |  | ||||||
| 
 |  | ||||||
| 			const out = await res.json<any>() |  | ||||||
| 			assert.equal(typeof out.id, 'number') |  | ||||||
| 			assert.equal(out.endpoint, data.subscription.endpoint) |  | ||||||
| 		}) |  | ||||||
| 
 |  | ||||||
| 		test('create subscription', async () => { |  | ||||||
| 			const db = await makeDB() |  | ||||||
| 			const client = await createTestClient(db) |  | ||||||
| 			const connectedActor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com') |  | ||||||
| 			const vapidKeys = await generateVAPIDKeys() |  | ||||||
| 
 |  | ||||||
| 			const data: any = { |  | ||||||
| 				subscription: { |  | ||||||
| 					endpoint: 'https://endpoint.com', |  | ||||||
| 					keys: { |  | ||||||
| 						p256dh: 'p256dh', |  | ||||||
| 						auth: 'auth', |  | ||||||
| 					}, |  | ||||||
| 				}, |  | ||||||
| 				data: { |  | ||||||
| 					alerts: {}, |  | ||||||
| 					policy: 'all', |  | ||||||
| 				}, |  | ||||||
| 			} |  | ||||||
| 			const req = new Request('https://example.com', { |  | ||||||
| 				method: 'POST', |  | ||||||
| 				body: JSON.stringify(data), |  | ||||||
| 			}) |  | ||||||
| 
 |  | ||||||
| 			const res = await subscription.handlePostRequest(db, req, connectedActor, client.id, vapidKeys) |  | ||||||
| 			assert.equal(res.status, 200) |  | ||||||
| 
 |  | ||||||
| 			const row: any = await db.prepare('SELECT * FROM subscriptions').first() |  | ||||||
| 			assert.equal(row.actor_id, connectedActor.id.toString()) |  | ||||||
| 			assert.equal(row.client_id, client.id) |  | ||||||
| 			assert.equal(row.endpoint, data.subscription.endpoint) |  | ||||||
| 		}) |  | ||||||
| 
 |  | ||||||
| 		test('create subscriptions only creates one', async () => { |  | ||||||
| 			const db = await makeDB() |  | ||||||
| 			const client = await createTestClient(db) |  | ||||||
| 			const connectedActor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com') |  | ||||||
| 			const vapidKeys = await generateVAPIDKeys() |  | ||||||
| 
 |  | ||||||
| 			const data: any = { |  | ||||||
| 				subscription: { |  | ||||||
| 					endpoint: 'https://endpoint.com', |  | ||||||
| 					keys: { |  | ||||||
| 						p256dh: 'p256dh', |  | ||||||
| 						auth: 'auth', |  | ||||||
| 					}, |  | ||||||
| 				}, |  | ||||||
| 				data: { |  | ||||||
| 					alerts: {}, |  | ||||||
| 					policy: 'all', |  | ||||||
| 				}, |  | ||||||
| 			} |  | ||||||
| 			await createSubscription(db, connectedActor, client, data) |  | ||||||
| 
 |  | ||||||
| 			const req = new Request('https://example.com', { |  | ||||||
| 				method: 'POST', |  | ||||||
| 				body: JSON.stringify(data), |  | ||||||
| 			}) |  | ||||||
| 
 |  | ||||||
| 			const res = await subscription.handlePostRequest(db, req, connectedActor, client.id, vapidKeys) |  | ||||||
| 			assert.equal(res.status, 200) |  | ||||||
| 
 |  | ||||||
| 			const { count } = await db.prepare('SELECT count(*) as count FROM subscriptions').first<{ count: number }>() |  | ||||||
| 			assert.equal(count, 1) |  | ||||||
| 		}) |  | ||||||
| 	}) |  | ||||||
| 
 |  | ||||||
| 	test('mutes returns an empty array', async () => { | 	test('mutes returns an empty array', async () => { | ||||||
| 		const res = await mutes.onRequest() | 		const res = await mutes.onRequest() | ||||||
| 		assert.equal(res.status, 200) | 		assert.equal(res.status, 200) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,171 @@ | ||||||
|  | import { createSubscription } from '../../src/mastodon/subscription' | ||||||
|  | import { createPerson } from 'wildebeest/backend/src/activitypub/actors' | ||||||
|  | import { strict as assert } from 'node:assert/strict' | ||||||
|  | import { makeDB, createTestClient, generateVAPIDKeys } from '../utils' | ||||||
|  | import * as subscription from 'wildebeest/functions/api/v1/push/subscription' | ||||||
|  | 
 | ||||||
|  | const userKEK = 'test_kek21' | ||||||
|  | const domain = 'cloudflare.com' | ||||||
|  | 
 | ||||||
|  | describe('Mastodon APIs', () => { | ||||||
|  | 	describe('subscriptions', () => { | ||||||
|  | 		test('get non existing subscription', async () => { | ||||||
|  | 			const db = await makeDB() | ||||||
|  | 			const vapidKeys = await generateVAPIDKeys() | ||||||
|  | 			const req = new Request('https://example.com') | ||||||
|  | 			const client = await createTestClient(db) | ||||||
|  | 			const connectedActor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com') | ||||||
|  | 
 | ||||||
|  | 			const res = await subscription.handleGetRequest(db, req, connectedActor, client.id, vapidKeys) | ||||||
|  | 			assert.equal(res.status, 404) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		test('get existing subscription', async () => { | ||||||
|  | 			const db = await makeDB() | ||||||
|  | 			const vapidKeys = await generateVAPIDKeys() | ||||||
|  | 			const req = new Request('https://example.com') | ||||||
|  | 			const client = await createTestClient(db) | ||||||
|  | 			const connectedActor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com') | ||||||
|  | 
 | ||||||
|  | 			const data: any = { | ||||||
|  | 				subscription: { | ||||||
|  | 					endpoint: 'https://endpoint.com', | ||||||
|  | 					keys: { | ||||||
|  | 						p256dh: 'p256dh', | ||||||
|  | 						auth: 'auth', | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				data: { | ||||||
|  | 					alerts: { | ||||||
|  | 						follow: false, | ||||||
|  | 						favourite: true, | ||||||
|  | 						reblog: false, | ||||||
|  | 						poll: true, | ||||||
|  | 					}, | ||||||
|  | 					policy: 'followed', | ||||||
|  | 				}, | ||||||
|  | 			} | ||||||
|  | 			await createSubscription(db, connectedActor, client, data) | ||||||
|  | 
 | ||||||
|  | 			const res = await subscription.handleGetRequest(db, req, connectedActor, client.id, vapidKeys) | ||||||
|  | 			assert.equal(res.status, 200) | ||||||
|  | 
 | ||||||
|  | 			const out = await res.json<any>() | ||||||
|  | 			assert.equal(typeof out.id, 'number') | ||||||
|  | 			assert.equal(out.endpoint, data.subscription.endpoint) | ||||||
|  | 			assert.equal(out.alerts.follow, false) | ||||||
|  | 			assert.equal(out.alerts.favourite, true) | ||||||
|  | 			assert.equal(out.alerts.reblog, false) | ||||||
|  | 			assert.equal(out.alerts.poll, true) | ||||||
|  | 			assert.equal(out.policy, 'followed') | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		test('create subscription', async () => { | ||||||
|  | 			const db = await makeDB() | ||||||
|  | 			const vapidKeys = await generateVAPIDKeys() | ||||||
|  | 			const client = await createTestClient(db) | ||||||
|  | 			const connectedActor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com') | ||||||
|  | 
 | ||||||
|  | 			const data: any = { | ||||||
|  | 				subscription: { | ||||||
|  | 					endpoint: 'https://endpoint.com', | ||||||
|  | 					keys: { | ||||||
|  | 						p256dh: 'p256dh', | ||||||
|  | 						auth: 'auth', | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				data: { | ||||||
|  | 					alerts: { | ||||||
|  | 						poll: false, | ||||||
|  | 						status: true, | ||||||
|  | 					}, | ||||||
|  | 					policy: 'all', | ||||||
|  | 				}, | ||||||
|  | 			} | ||||||
|  | 			const req = new Request('https://example.com', { | ||||||
|  | 				method: 'POST', | ||||||
|  | 				body: JSON.stringify(data), | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			const res = await subscription.handlePostRequest(db, req, connectedActor, client.id, vapidKeys) | ||||||
|  | 			assert.equal(res.status, 200) | ||||||
|  | 
 | ||||||
|  | 			const out = await res.json<any>() | ||||||
|  | 			assert.equal(out.alerts.mention, true) | ||||||
|  | 			assert.equal(out.alerts.status, true) // default to true
 | ||||||
|  | 			assert.equal(out.alerts.poll, false) | ||||||
|  | 
 | ||||||
|  | 			const row: any = await db.prepare('SELECT * FROM subscriptions').first() | ||||||
|  | 			assert.equal(row.actor_id, connectedActor.id.toString()) | ||||||
|  | 			assert.equal(row.client_id, client.id) | ||||||
|  | 			assert.equal(row.endpoint, data.subscription.endpoint) | ||||||
|  | 			assert.equal(row.alert_poll, 0) | ||||||
|  | 			assert.equal(row.alert_mention, 1) | ||||||
|  | 			assert.equal(row.alert_status, 1) // default to true
 | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		test('create subscriptions only creates one', async () => { | ||||||
|  | 			const db = await makeDB() | ||||||
|  | 			const vapidKeys = await generateVAPIDKeys() | ||||||
|  | 			const client = await createTestClient(db) | ||||||
|  | 			const connectedActor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com') | ||||||
|  | 
 | ||||||
|  | 			const data: any = { | ||||||
|  | 				subscription: { | ||||||
|  | 					endpoint: 'https://endpoint.com', | ||||||
|  | 					keys: { | ||||||
|  | 						p256dh: 'p256dh', | ||||||
|  | 						auth: 'auth', | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				data: { | ||||||
|  | 					alerts: {}, | ||||||
|  | 					policy: 'all', | ||||||
|  | 				}, | ||||||
|  | 			} | ||||||
|  | 			await createSubscription(db, connectedActor, client, data) | ||||||
|  | 
 | ||||||
|  | 			const req = new Request('https://example.com', { | ||||||
|  | 				method: 'POST', | ||||||
|  | 				body: JSON.stringify(data), | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			const res = await subscription.handlePostRequest(db, req, connectedActor, client.id, vapidKeys) | ||||||
|  | 			assert.equal(res.status, 200) | ||||||
|  | 
 | ||||||
|  | 			const { count } = await db.prepare('SELECT count(*) as count FROM subscriptions').first<{ count: number }>() | ||||||
|  | 			assert.equal(count, 1) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		test('subscriptions auto increment', async () => { | ||||||
|  | 			const db = await makeDB() | ||||||
|  | 			const connectedActor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com') | ||||||
|  | 
 | ||||||
|  | 			const data: any = { | ||||||
|  | 				subscription: { | ||||||
|  | 					endpoint: 'https://endpoint.com', | ||||||
|  | 					keys: { | ||||||
|  | 						p256dh: 'p256dh', | ||||||
|  | 						auth: 'auth', | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				data: { | ||||||
|  | 					alerts: {}, | ||||||
|  | 					policy: 'all', | ||||||
|  | 				}, | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			const client1 = await createTestClient(db) | ||||||
|  | 			const sub1 = await createSubscription(db, connectedActor, client1, data) | ||||||
|  | 			assert.equal(sub1.id, 1) | ||||||
|  | 
 | ||||||
|  | 			const client2 = await createTestClient(db) | ||||||
|  | 			const sub2 = await createSubscription(db, connectedActor, client2, data) | ||||||
|  | 			assert.equal(sub2.id, 2) | ||||||
|  | 
 | ||||||
|  | 			const client3 = await createTestClient(db) | ||||||
|  | 			const sub3 = await createSubscription(db, connectedActor, client3, data) | ||||||
|  | 			assert.equal(sub3.id, 3) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | }) | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| import { strict as assert } from 'node:assert/strict' | import { strict as assert } from 'node:assert/strict' | ||||||
|  | import type { JWK } from 'wildebeest/backend/src/webpush/jwk' | ||||||
| import type { Cache } from 'wildebeest/backend/src/cache' | import type { Cache } from 'wildebeest/backend/src/cache' | ||||||
| import type { Queue } from 'wildebeest/backend/src/types/queue' | import type { Queue } from 'wildebeest/backend/src/types/queue' | ||||||
| import { createClient } from 'wildebeest/backend/src/mastodon/client' | import { createClient } from 'wildebeest/backend/src/mastodon/client' | ||||||
|  | @ -117,3 +118,12 @@ export function isUUID(v: string): boolean { | ||||||
| 	} | 	} | ||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export async function generateVAPIDKeys(): Promise<JWK> { | ||||||
|  | 	const keyPair = (await crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, [ | ||||||
|  | 		'sign', | ||||||
|  | 		'verify', | ||||||
|  | 	])) as CryptoKeyPair | ||||||
|  | 	const jwk = (await crypto.subtle.exportKey('jwk', keyPair.privateKey)) as JWK | ||||||
|  | 	return jwk | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ import * as errors from 'wildebeest/backend/src/errors' | ||||||
| import { VAPIDPublicKey } from 'wildebeest/backend/src/mastodon/subscription' | import { VAPIDPublicKey } from 'wildebeest/backend/src/mastodon/subscription' | ||||||
| 
 | 
 | ||||||
| export const onRequestGet: PagesFunction<Env, any, ContextData> = async ({ request, env, data }) => { | export const onRequestGet: PagesFunction<Env, any, ContextData> = async ({ request, env, data }) => { | ||||||
| 	return handleGetRequest(env.DATABASE, request, data.connectedActor, data.clientId) | 	return handleGetRequest(env.DATABASE, request, data.connectedActor, data.clientId, getVAPIDKeys(env)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const onRequestPost: PagesFunction<Env, any, ContextData> = async ({ request, env, data }) => { | export const onRequestPost: PagesFunction<Env, any, ContextData> = async ({ request, env, data }) => { | ||||||
|  | @ -23,7 +23,13 @@ const headers = { | ||||||
| 	'content-type': 'application/json; charset=utf-8', | 	'content-type': 'application/json; charset=utf-8', | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export async function handleGetRequest(db: D1Database, request: Request, connectedActor: Actor, clientId: string) { | export async function handleGetRequest( | ||||||
|  | 	db: D1Database, | ||||||
|  | 	request: Request, | ||||||
|  | 	connectedActor: Actor, | ||||||
|  | 	clientId: string, | ||||||
|  | 	vapidKeys: JWK | ||||||
|  | ) { | ||||||
| 	const client = await getClientById(db, clientId) | 	const client = await getClientById(db, clientId) | ||||||
| 	if (client === null) { | 	if (client === null) { | ||||||
| 		return errors.clientUnknown() | 		return errors.clientUnknown() | ||||||
|  | @ -35,20 +41,14 @@ export async function handleGetRequest(db: D1Database, request: Request, connect | ||||||
| 		return new Response('', { status: 404 }) | 		return new Response('', { status: 404 }) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const res = { | 	const vapidKey = VAPIDPublicKey(vapidKeys) | ||||||
| 		id: 4, |  | ||||||
| 		endpoint: subscription.gateway.endpoint, |  | ||||||
| 		alerts: { |  | ||||||
| 			follow: true, |  | ||||||
| 			favourite: true, |  | ||||||
| 			reblog: true, |  | ||||||
| 			mention: true, |  | ||||||
| 			poll: true, |  | ||||||
| 		}, |  | ||||||
| 		policy: 'all', |  | ||||||
| 
 | 
 | ||||||
| 		// FIXME: stub value
 | 	const res = { | ||||||
| 		server_key: 'TODO', | 		id: subscription.id, | ||||||
|  | 		endpoint: subscription.gateway.endpoint, | ||||||
|  | 		alerts: subscription.alerts, | ||||||
|  | 		policy: subscription.policy, | ||||||
|  | 		server_key: vapidKey, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return new Response(JSON.stringify(res), { headers }) | 	return new Response(JSON.stringify(res), { headers }) | ||||||
|  | @ -77,16 +77,10 @@ export async function handlePostRequest( | ||||||
| 	const vapidKey = VAPIDPublicKey(vapidKeys) | 	const vapidKey = VAPIDPublicKey(vapidKeys) | ||||||
| 
 | 
 | ||||||
| 	const res = { | 	const res = { | ||||||
| 		id: 4, | 		id: subscription.id, | ||||||
| 		endpoint: data.subscription.endpoint, | 		endpoint: subscription.gateway.endpoint, | ||||||
| 		alerts: { | 		alerts: subscription.alerts, | ||||||
| 			follow: true, | 		policy: subscription.policy, | ||||||
| 			favourite: true, |  | ||||||
| 			reblog: true, |  | ||||||
| 			mention: true, |  | ||||||
| 			poll: true, |  | ||||||
| 		}, |  | ||||||
| 		policy: 'all', |  | ||||||
| 		server_key: vapidKey, | 		server_key: vapidKey, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,28 @@ | ||||||
|  | -- Migration number: 0007 	 2023-02-15T11:01:46.585Z | ||||||
|  | 
 | ||||||
|  | DROP table subscriptions; | ||||||
|  | 
 | ||||||
|  | CREATE TABLE subscriptions ( | ||||||
|  |   id INTEGER PRIMARY KEY AUTOINCREMENT, | ||||||
|  |   actor_id TEXT NOT NULL, | ||||||
|  |   client_id TEXT NOT NULL, | ||||||
|  |   endpoint TEXT NULL NOT NULL, | ||||||
|  |   key_p256dh TEXT NOT NULL, | ||||||
|  |   key_auth TEXT NOT NULL, | ||||||
|  |   alert_mention INTEGER NOT NULL, | ||||||
|  |   alert_status INTEGER NOT NULL, | ||||||
|  |   alert_reblog INTEGER NOT NULL, | ||||||
|  |   alert_follow INTEGER NOT NULL, | ||||||
|  |   alert_follow_request INTEGER NOT NULL, | ||||||
|  |   alert_favourite INTEGER NOT NULL, | ||||||
|  |   alert_poll INTEGER NOT NULL, | ||||||
|  |   alert_update INTEGER NOT NULL, | ||||||
|  |   alert_admin_sign_up INTEGER NOT NULL, | ||||||
|  |   alert_admin_report INTEGER NOT NULL, | ||||||
|  |   policy TEXT NOT NULL, | ||||||
|  |   cdate DATETIME NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), | ||||||
|  | 
 | ||||||
|  |   UNIQUE(actor_id, client_id) | ||||||
|  |   FOREIGN KEY(actor_id)  REFERENCES actors(id), | ||||||
|  |   FOREIGN KEY(client_id) REFERENCES clients(id) | ||||||
|  | ); | ||||||
		Ładowanie…
	
		Reference in New Issue
	
	 Sven Sauleau
						Sven Sauleau