fix: added api routes for push registration

This commit is contained in:
Brandon
2023-04-18 00:39:42 -04:00
committed by Brandon Cohen
parent e6c054fb73
commit 23dca554ca
4 changed files with 158 additions and 36 deletions

View File

@@ -3548,6 +3548,47 @@ paths:
responses:
'204':
description: Successfully registered push subscription
/user/{key}/pushSubscription:
get:
summary: Get web push notification settings for a user
description: |
Returns web push notification settings for a user in a JSON object.
tags:
- users
parameters:
- in: path
name: key
required: true
schema:
type: string
responses:
'200':
description: User web push notification settings in JSON
content:
application/json:
schema:
type: object
properties:
endpoint:
type: string
p256dh:
type: string
auth:
type: string
delete:
summary: Delete user push subscription by key
description: Deletes the user push subscription with the provided key.
tags:
- users
parameters:
- in: path
name: key
required: true
schema:
type: string
responses:
'204':
description: Successfully removed user push subscription
/user/{userId}:
get:
summary: Get user by ID

View File

@@ -138,6 +138,25 @@ router.post(
}
);
router.get<{ key: string }>(
'/:key/pushSubscription',
async (req, res, next) => {
try {
const userPushSubRepository = getRepository(UserPushSubscription);
const userPushSub = await userPushSubRepository.findOneOrFail({
where: {
p256dh: req.params.key,
},
});
return res.status(200).json(userPushSub);
} catch (e) {
next({ status: 404, message: 'User subscription not found.' });
}
}
);
router.post<
never,
unknown,
@@ -180,6 +199,32 @@ router.post<
}
});
router.delete<{ key: string }>(
'/:key/pushSubscription',
async (req, res, next) => {
try {
const userPushSubRepository = getRepository(UserPushSubscription);
const userPushSub = await userPushSubRepository.findOneOrFail({
where: { p256dh: req.params.key },
});
await userPushSubRepository.remove(userPushSub);
return res.status(204).send();
} catch (e) {
logger.error('Something went wrong deleting the user push subcription', {
label: 'API',
key: req.params.key,
errorMessage: e.message,
});
return next({
status: 500,
message: 'User push subcription not found',
});
}
}
);
router.get<{ id: string }>('/:id', async (req, res, next) => {
try {
const userRepository = getRepository(User);

View File

@@ -39,10 +39,33 @@ const UserDropdown = () => {
const { user, revalidate } = useUser();
const logout = async () => {
const response = await axios.post('/api/v1/auth/logout');
if ('serviceWorker' in navigator && user?.id) {
navigator.serviceWorker.getRegistration('/sw.js').then((registration) => {
registration?.pushManager
.getSubscription()
.then(async (subscription) => {
subscription
?.unsubscribe()
.then(async () => {
const parsedSub = JSON.parse(JSON.stringify(subscription));
await axios.delete(
`/api/v1/user/${parsedSub.keys.p256dh}/pushSubscription`
);
const response = await axios.post('/api/v1/auth/logout');
if (response.data?.status === 'ok') {
revalidate();
if (response.data?.status === 'ok') {
revalidate();
}
})
.catch(function (error) {
// eslint-disable-next-line no-console
console.log(
'[SW] Failure unsubscribing to push manager, error:',
error
);
});
});
});
}
};

View File

@@ -11,6 +11,7 @@ import {
CloudArrowDownIcon,
CloudArrowUpIcon,
} from '@heroicons/react/24/solid';
import type { UserPushSubscription } from '@server/entity/UserPushSubscription';
import type { UserSettingsNotificationsResponse } from '@server/interfaces/api/userSettingsInterfaces';
import axios from 'axios';
import { Form, Formik } from 'formik';
@@ -49,11 +50,6 @@ const UserWebPushSettings = () => {
navigator.serviceWorker
.getRegistration('/sw.js')
.then(async (registration) => {
const permissionState =
await registration?.pushManager.permissionState({
userVisibleOnly: true,
});
if (currentSettings.enablePushRegistration) {
const sub = await registration?.pushManager.subscribe({
userVisibleOnly: true,
@@ -63,13 +59,11 @@ const UserWebPushSettings = () => {
const parsedSub = JSON.parse(JSON.stringify(sub));
if (parsedSub.keys.p256dh && parsedSub.keys.auth) {
if (permissionState === 'prompt') {
await axios.post('/api/v1/user/registerPushSubscription', {
endpoint: parsedSub.endpoint,
p256dh: parsedSub.keys.p256dh,
auth: parsedSub.keys.auth,
});
}
await axios.post('/api/v1/user/registerPushSubscription', {
endpoint: parsedSub.endpoint,
p256dh: parsedSub.keys.p256dh,
auth: parsedSub.keys.auth,
});
setWebPushEnabled(true);
}
}
@@ -93,7 +87,13 @@ const UserWebPushSettings = () => {
.then(async (subscription) => {
subscription
?.unsubscribe()
.then(() => setWebPushEnabled(false))
.then(async () => {
const parsedSub = JSON.parse(JSON.stringify(subscription));
await axios.delete(
`/api/v1/user/${parsedSub.keys.p256dh}/pushSubscription`
);
setWebPushEnabled(false);
})
.catch(function (error) {
// eslint-disable-next-line no-console
console.log(
@@ -113,11 +113,22 @@ const UserWebPushSettings = () => {
navigator.serviceWorker
.getRegistration('/sw.js')
.then(async (registration) => {
await registration?.pushManager.getSubscription().then((status) => {
if (status) {
setWebPushEnabled(true);
}
});
await registration?.pushManager
.getSubscription()
.then(async (subscription) => {
if (subscription) {
const parsedKey = JSON.parse(JSON.stringify(subscription));
const currentUserPushSub =
await axios.get<UserPushSubscription>(
`/api/v1/user/${parsedKey.keys.p256dh}/pushSubscription`
);
if (currentUserPushSub.data.p256dh !== parsedKey.keys.p256dh) {
return;
}
setWebPushEnabled(true);
}
});
})
.catch(function (error) {
// eslint-disable-next-line no-console
@@ -198,7 +209,7 @@ const UserWebPushSettings = () => {
<div className="flex justify-end">
<span className="ml-3 inline-flex rounded-md shadow-sm">
<Button
buttonType={`${webPushEnabled ? 'danger' : 'warning'}`}
buttonType={`${webPushEnabled ? 'danger' : 'primary'}`}
type="button"
onClick={() =>
webPushEnabled
@@ -218,20 +229,22 @@ const UserWebPushSettings = () => {
</span>
</Button>
</span>
<span className="ml-3 inline-flex rounded-md shadow-sm">
<Button
buttonType="primary"
type="submit"
disabled={isSubmitting || !isValid}
>
<ArrowDownOnSquareIcon />
<span>
{isSubmitting
? intl.formatMessage(globalMessages.saving)
: intl.formatMessage(globalMessages.save)}
</span>
</Button>
</span>
{webPushEnabled && (
<span className="ml-3 inline-flex rounded-md shadow-sm">
<Button
buttonType="primary"
type="submit"
disabled={isSubmitting || !isValid}
>
<ArrowDownOnSquareIcon />
<span>
{isSubmitting
? intl.formatMessage(globalMessages.saving)
: intl.formatMessage(globalMessages.save)}
</span>
</Button>
</span>
)}
</div>
</div>
</Form>