Skip to content

Commit

Permalink
fix: update favorites plannings, security check
Browse files Browse the repository at this point in the history
  • Loading branch information
kernoeb committed Sep 20, 2023
1 parent 03aaafc commit 3043135
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 28 deletions.
12 changes: 6 additions & 6 deletions components/DialogSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -471,14 +471,14 @@ export default {
const subscription = await registration.pushManager.getSubscription()
await subscription.unsubscribe()
await fetch('/api/v1/subscriptions/unsubscribe', {
method: 'POST',
body: JSON.stringify(subscription),
await this.$axios.$post('/api/v1/subscriptions/unsubscribe', subscription, {
headers: {
'content-type': 'application/json'
}
})
localStorage.removeItem('subscription')
console.log('Unregistered push')
this.notifications = false
this.loadingSubscription = false
Expand Down Expand Up @@ -509,10 +509,10 @@ export default {
subscription.plannings = []
}
localStorage.setItem('subscription', JSON.stringify(subscription))
console.log('Sending push')
await fetch('/api/v1/subscriptions/subscribe', {
method: 'POST',
body: JSON.stringify(subscription),
await this.$axios.$post('/api/v1/subscriptions/subscribe', subscription, {
headers: {
'content-type': 'application/json'
}
Expand Down
15 changes: 12 additions & 3 deletions components/SelectPlanning.vue
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,6 @@ export default {
const final = [...new Set(tmp)].filter(v => !!v)
this.favorites = final
this.$cookies.set('favorites', final.join(','), { maxAge: 2147483646 })
this.getNames()
this.$nextTick(() => {
this.refreshFavorites()
})
Expand Down Expand Up @@ -443,8 +442,6 @@ export default {
this.menuGroup = false
},
refreshFavorites () {
// TODO update push notifications
try {
this.favorites = (this.$cookies?.get('favorites')?.split(',') || []).filter(v => !!v)
Expand All @@ -457,6 +454,18 @@ export default {
this.getNames()
try {
const subscription = JSON.parse(localStorage.getItem('subscription') || '{}')
if (subscription && subscription.endpoint) {
const plannings = [...this.favorites || []]
this.$axios.$put('/api/v1/subscriptions/update-plannings ', { subscription, plannings }).catch((err) => {
console.log('Error updating favorites for notifications', err)
})
}
} catch (err) {
console.log('Error updating favorites for notifications', err)
}
if (this.$cookies?.get('favorites') === '') this.$cookies.remove('favorites')
if (this.$cookies?.get('groupFavorites') === '') this.$cookies.remove('groupFavorites')
} catch (err) {}
Expand Down
9 changes: 4 additions & 5 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ if (process.env.NODE_ENV !== 'test') {

// Get distinct of subscriptions plannings
const subscriptions = await Subscription.distinct('plannings')
console.log('Number of active subscriptions', subscriptions.length)
logger.info('[Subscriptions] Number of active plannings', subscriptions.length)

if (subscriptions.length) {
for (const planning of subscriptions) {
Expand All @@ -107,6 +107,7 @@ if (process.env.NODE_ENV !== 'test') {

const diff = dtstart - now
const tenMinutes = 1000 * 60 * 10
// const thirtyMinutes = 1000 * 60 * 30
// const twelveHours = 1000 * 60 * 60 * 12

if (diff <= tenMinutes) {
Expand All @@ -116,7 +117,7 @@ if (process.env.NODE_ENV !== 'test') {
const payload = JSON.stringify({
title: `Cours dans ${Math.round(diff / 1000 / 60)} minutes`,
body: `- ${cleanName(nextEvent.summary.value)}\n- ${cleanLocation(nextEvent.location.value)}\n- ${cleanDescription(nextEvent.description.value)}`,
icon: 'https://planningsup.app/favicon.ico'
icon: '/favicon.ico'
})
const date = new Date(dtstart)

Expand All @@ -130,9 +131,7 @@ if (process.env.NODE_ENV !== 'test') {
continue
}

await retry(async () => {
await sendNotification(subscription, payload, true)
}, 3) // Retry 3 times
await retry(async () => await sendNotification(subscription, payload, true), 2) // Retry 2 times
.then(() => updateSubscription(subscription, { id, date, status: 'sent' }))
.catch(() => updateSubscription(subscription, { id, date, status: 'error' }))
}
Expand Down
38 changes: 24 additions & 14 deletions server/routes/subscriptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,40 @@ const asyncWrap = require('async-wrapper-express-ts')
const { sendNotification } = require('../util/notifications')
const { Subscription } = require('../models/subscriptions')

// TODO clean up expired subscriptions every 1 hour
const checkPayload = (req, res, next) => {
if (!req.body || !req.body.plannings) return res.status(400).json({ message: 'Missing plannings' })
if (!Array.isArray(req.body.plannings) || !req.body.plannings.every(p => typeof p === 'string')) return res.status(400).json({ message: 'Invalid plannings' })
next()
}

router.post('/subscribe', asyncWrap(async (req, res) => {
const subscriptionBody = req.body
if (!subscriptionBody || !subscriptionBody.endpoint) return res.status(400).json({ message: 'Invalid subscription' })
if (typeof subscriptionBody.endpoint !== 'string') return res.status(400).json({ message: 'Invalid subscription' })
if (!subscriptionBody.plannings) return res.status(400).json({ message: 'Missing plannings' })
router.post('/subscribe', checkPayload, asyncWrap(async (req, res) => {
const subscription = req.body
if (!subscription || !subscription.endpoint || typeof subscription.endpoint !== 'string') return res.status(400).json({ message: 'Invalid subscription' })

const payload = JSON.stringify({ title: `Abonnement réussi pour ${subscriptionBody.plannings.length} planning(s)`, body: 'Vous recevrez une notification en temps voulu :)', icon: '/favicon.ico' })
await sendNotification(subscriptionBody, payload, true)
const payload = JSON.stringify({ title: `Abonnement réussi pour ${subscription.plannings.length} planning(s)`, body: 'Vous recevrez une notification en temps voulu :)', icon: '/favicon.ico' })
await sendNotification(subscription, payload, true)

const subscription = await Subscription.findOneAndUpdate({ endpoint: subscriptionBody.endpoint }, subscriptionBody, { new: true, upsert: true, runValidators: true })
const result = await Subscription.findOneAndUpdate({ endpoint: subscription.endpoint }, subscription, { new: true, upsert: true, runValidators: true })

return res.status(201).json({ message: 'Subscribed', subscription })
return res.status(201).json({ message: 'Subscribed', subscription: result })
}))

router.post('/unsubscribe', asyncWrap(async (req, res) => {
const subscriptionBody = req.body
if (!subscriptionBody || !subscriptionBody.endpoint) return res.status(400).json({ message: 'Invalid subscription' })
if (typeof subscriptionBody.endpoint !== 'string') return res.status(400).json({ message: 'Invalid subscription' })
const subscription = req.body
if (!subscription || !subscription.endpoint || typeof subscription.endpoint !== 'string') return res.status(400).json({ message: 'Invalid subscription' })

await Subscription.deleteOne({ endpoint: subscriptionBody.endpoint }, { runValidators: true })
await Subscription.deleteOne({ endpoint: subscription.endpoint }, { runValidators: true })

return res.status(200).json({ message: 'Unsubscribed' })
}))

router.put('/update-plannings', checkPayload, asyncWrap(async (req, res) => {
const { subscription, plannings } = req.body
if (!subscription || !subscription.endpoint || typeof subscription.endpoint !== 'string') return res.status(400).json({ message: 'Invalid subscription' })

await Subscription.updateOne({ endpoint: subscription.endpoint }, { $set: { plannings } })

return res.status(201) // do not leak
}))

module.exports = router

0 comments on commit 3043135

Please sign in to comment.