mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
Add endpoint to GET a push subscription
This commit is contained in:
parent
b3928bac45
commit
4019099c57
2 changed files with 67 additions and 19 deletions
|
|
@ -73,7 +73,7 @@ import {
|
|||
updateConfigController,
|
||||
} from '@/controllers/api/pleroma.ts';
|
||||
import { preferencesController } from '@/controllers/api/preferences.ts';
|
||||
import { pushSubscribeController } from '@/controllers/api/push.ts';
|
||||
import { getSubscriptionController, pushSubscribeController } from '@/controllers/api/push.ts';
|
||||
import { deleteReactionController, reactionController, reactionsController } from '@/controllers/api/reactions.ts';
|
||||
import { relayController } from '@/controllers/nostr/relay.ts';
|
||||
import {
|
||||
|
|
@ -281,6 +281,7 @@ app.get('/api/v1/mutes', requireSigner, mutesController);
|
|||
app.get('/api/v1/markers', requireProof(), markersController);
|
||||
app.post('/api/v1/markers', requireProof(), updateMarkersController);
|
||||
|
||||
app.get('/api/v1/push/subscription', requireSigner, getSubscriptionController);
|
||||
app.post('/api/v1/push/subscription', requireProof(), pushSubscribeController);
|
||||
|
||||
app.get('/api/v1/pleroma/statuses/:id{[0-9a-f]{64}}/reactions', reactionsController);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,15 @@ import { Storages } from '@/storages.ts';
|
|||
import { parseBody } from '@/utils/api.ts';
|
||||
import { getTokenHash } from '@/utils/auth.ts';
|
||||
|
||||
/** https://docs.joinmastodon.org/entities/WebPushSubscription/ */
|
||||
interface MastodonPushSubscription {
|
||||
id: string;
|
||||
endpoint: string;
|
||||
server_key: string;
|
||||
alerts: Record<string, boolean>;
|
||||
policy: 'all' | 'followed' | 'follower' | 'none';
|
||||
}
|
||||
|
||||
const pushSubscribeSchema = z.object({
|
||||
subscription: z.object({
|
||||
endpoint: z.string().url(),
|
||||
|
|
@ -33,20 +42,13 @@ const pushSubscribeSchema = z.object({
|
|||
});
|
||||
|
||||
export const pushSubscribeController: AppController = async (c) => {
|
||||
const BEARER_REGEX = new RegExp(`^Bearer (${nip19.BECH32_REGEX.source})$`);
|
||||
const vapidPublicKey = await Conf.vapidPublicKey;
|
||||
|
||||
const header = c.req.header('authorization');
|
||||
const match = header?.match(BEARER_REGEX);
|
||||
|
||||
if (!match) {
|
||||
return c.json({ error: 'Unauthorized' }, 401);
|
||||
if (!vapidPublicKey) {
|
||||
return c.json({ error: 'The administrator of this server has not enabled Web Push notifications.' }, 404);
|
||||
}
|
||||
|
||||
const [_, accessToken] = match;
|
||||
|
||||
if (!accessToken.startsWith('token1')) {
|
||||
return c.json({ error: 'Unauthorized' }, 401);
|
||||
}
|
||||
const accessToken = getAccessToken(c.req.raw);
|
||||
|
||||
const kysely = await Storages.kysely();
|
||||
const signer = c.get('signer')!;
|
||||
|
|
@ -82,11 +84,56 @@ export const pushSubscribeController: AppController = async (c) => {
|
|||
.executeTakeFirstOrThrow();
|
||||
});
|
||||
|
||||
return c.json({
|
||||
id,
|
||||
return c.json(
|
||||
{
|
||||
id: id.toString(),
|
||||
endpoint: subscription.endpoint,
|
||||
alerts: data?.alerts ?? {},
|
||||
policy: data?.policy ?? 'all',
|
||||
server_key: await Conf.vapidPublicKey,
|
||||
});
|
||||
server_key: vapidPublicKey,
|
||||
} satisfies MastodonPushSubscription,
|
||||
);
|
||||
};
|
||||
|
||||
export const getSubscriptionController: AppController = async (c) => {
|
||||
const vapidPublicKey = await Conf.vapidPublicKey;
|
||||
|
||||
if (!vapidPublicKey) {
|
||||
return c.json({ error: 'The administrator of this server has not enabled Web Push notifications.' }, 404);
|
||||
}
|
||||
|
||||
const accessToken = getAccessToken(c.req.raw);
|
||||
|
||||
const kysely = await Storages.kysely();
|
||||
const tokenHash = await getTokenHash(accessToken as `token1${string}`);
|
||||
|
||||
const row = await kysely
|
||||
.selectFrom('push_subscriptions')
|
||||
.selectAll()
|
||||
.where('token_hash', '=', tokenHash)
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
return c.json(
|
||||
{
|
||||
id: row.id.toString(),
|
||||
endpoint: row.endpoint,
|
||||
alerts: row.data?.alerts ?? {},
|
||||
policy: row.data?.policy ?? 'all',
|
||||
server_key: vapidPublicKey,
|
||||
} satisfies MastodonPushSubscription,
|
||||
);
|
||||
};
|
||||
|
||||
/** Get access token from HTTP headers, but only if it's a `token1`. Otherwise return undefined. */
|
||||
function getAccessToken(request: Request): `token1${string}` | undefined {
|
||||
const BEARER_REGEX = new RegExp(`^Bearer (${nip19.BECH32_REGEX.source})$`);
|
||||
|
||||
const authorization = request.headers.get('authorization');
|
||||
const match = authorization?.match(BEARER_REGEX);
|
||||
|
||||
const [_, accessToken] = match ?? [];
|
||||
|
||||
if (accessToken?.startsWith('token1')) {
|
||||
return accessToken as `token1${string}`;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue