mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-15 21:03:30 +00:00
UBERF-9230: Fix ses webpush (#7760)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
82a9204076
commit
90e8ca4e97
@ -15,6 +15,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import activity, { ActivityMessage, DocUpdateMessage } from '@hcengineering/activity'
|
import activity, { ActivityMessage, DocUpdateMessage } from '@hcengineering/activity'
|
||||||
|
import { Analytics } from '@hcengineering/analytics'
|
||||||
import chunter, { ChatMessage } from '@hcengineering/chunter'
|
import chunter, { ChatMessage } from '@hcengineering/chunter'
|
||||||
import contact, {
|
import contact, {
|
||||||
Employee,
|
Employee,
|
||||||
@ -39,7 +40,6 @@ import core, {
|
|||||||
generateId,
|
generateId,
|
||||||
MeasureContext,
|
MeasureContext,
|
||||||
MixinUpdate,
|
MixinUpdate,
|
||||||
RateLimiter,
|
|
||||||
Ref,
|
Ref,
|
||||||
RefTo,
|
RefTo,
|
||||||
SortingOrder,
|
SortingOrder,
|
||||||
@ -82,7 +82,6 @@ import serverView from '@hcengineering/server-view'
|
|||||||
import { markupToText, stripTags } from '@hcengineering/text-core'
|
import { markupToText, stripTags } from '@hcengineering/text-core'
|
||||||
import { encodeObjectURI } from '@hcengineering/view'
|
import { encodeObjectURI } from '@hcengineering/view'
|
||||||
import { workbenchId } from '@hcengineering/workbench'
|
import { workbenchId } from '@hcengineering/workbench'
|
||||||
import { Analytics } from '@hcengineering/analytics'
|
|
||||||
|
|
||||||
import { Content, ContextsCache, ContextsCacheKey, NotifyParams, NotifyResult } from './types'
|
import { Content, ContextsCache, ContextsCacheKey, NotifyParams, NotifyResult } from './types'
|
||||||
import {
|
import {
|
||||||
@ -92,6 +91,7 @@ import {
|
|||||||
getNotificationContent,
|
getNotificationContent,
|
||||||
getNotificationLink,
|
getNotificationLink,
|
||||||
getNotificationProviderControl,
|
getNotificationProviderControl,
|
||||||
|
getObjectSpace,
|
||||||
getTextPresenter,
|
getTextPresenter,
|
||||||
getUsersInfo,
|
getUsersInfo,
|
||||||
isAllowed,
|
isAllowed,
|
||||||
@ -103,8 +103,7 @@ import {
|
|||||||
replaceAll,
|
replaceAll,
|
||||||
toReceiverInfo,
|
toReceiverInfo,
|
||||||
updateNotifyContextsSpace,
|
updateNotifyContextsSpace,
|
||||||
type NotificationProviderControl,
|
type NotificationProviderControl
|
||||||
getObjectSpace
|
|
||||||
} from './utils'
|
} from './utils'
|
||||||
|
|
||||||
export function getPushCollaboratorTx (
|
export function getPushCollaboratorTx (
|
||||||
@ -602,13 +601,7 @@ export async function createPushNotification (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const limiter = new RateLimiter(5)
|
void sendPushToSubscription(sesURL, sesAuth, control, target, userSubscriptions, data)
|
||||||
for (const subscription of userSubscriptions) {
|
|
||||||
await limiter.add(async () => {
|
|
||||||
await sendPushToSubscription(sesURL, sesAuth, control, target, subscription, data)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
await limiter.waitProcessing()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendPushToSubscription (
|
async function sendPushToSubscription (
|
||||||
@ -616,11 +609,11 @@ async function sendPushToSubscription (
|
|||||||
sesAuth: string | undefined,
|
sesAuth: string | undefined,
|
||||||
control: TriggerControl,
|
control: TriggerControl,
|
||||||
targetUser: Ref<Account>,
|
targetUser: Ref<Account>,
|
||||||
subscription: PushSubscription,
|
subscriptions: PushSubscription[],
|
||||||
data: PushData
|
data: PushData
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const result: 'ok' | 'clear-push' = (
|
const result: Ref<PushSubscription>[] = (
|
||||||
await (
|
await (
|
||||||
await fetch(concatLink(sesURL, '/web-push'), {
|
await fetch(concatLink(sesURL, '/web-push'), {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
@ -629,15 +622,17 @@ async function sendPushToSubscription (
|
|||||||
...(sesAuth != null ? { Authorization: `Bearer ${sesAuth}` } : {})
|
...(sesAuth != null ? { Authorization: `Bearer ${sesAuth}` } : {})
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
subscription,
|
subscriptions,
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
).json()
|
).json()
|
||||||
).result
|
).result
|
||||||
if (result === 'clear-push') {
|
if (result.length > 0) {
|
||||||
const tx = control.txFactory.createTxRemoveDoc(subscription._class, subscription.space, subscription._id)
|
const domain = control.hierarchy.findDomain(notification.class.PushSubscription)
|
||||||
await control.apply(control.ctx, [tx])
|
if (domain !== undefined) {
|
||||||
|
await control.lowLevel.clean(control.ctx, domain, result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
control.ctx.info('Cannot send push notification to', { user: targetUser, err })
|
control.ctx.info('Cannot send push notification to', { user: targetUser, err })
|
||||||
|
@ -15,4 +15,8 @@
|
|||||||
|
|
||||||
import { main } from './main'
|
import { main } from './main'
|
||||||
|
|
||||||
void main()
|
void main().catch((err) => {
|
||||||
|
if (err != null) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import type { Ref } from '@hcengineering/core'
|
||||||
import { PushSubscription, type PushData } from '@hcengineering/notification'
|
import { PushSubscription, type PushData } from '@hcengineering/notification'
|
||||||
import type { Request, Response } from 'express'
|
import type { Request, Response } from 'express'
|
||||||
import webpush, { WebPushError } from 'web-push'
|
import webpush, { WebPushError } from 'web-push'
|
||||||
@ -22,25 +23,39 @@ import { SES } from './ses'
|
|||||||
import { Endpoint } from './types'
|
import { Endpoint } from './types'
|
||||||
|
|
||||||
const errorMessages = ['expired', 'Unregistered', 'No such subscription']
|
const errorMessages = ['expired', 'Unregistered', 'No such subscription']
|
||||||
async function sendPushToSubscription (subscription: PushSubscription, data: PushData): Promise<'ok' | 'clear-push'> {
|
async function sendPushToSubscription (
|
||||||
|
subscriptions: PushSubscription[],
|
||||||
|
data: PushData
|
||||||
|
): Promise<Ref<PushSubscription>[]> {
|
||||||
|
const result: Ref<PushSubscription>[] = []
|
||||||
|
for (const subscription of subscriptions) {
|
||||||
try {
|
try {
|
||||||
await webpush.sendNotification(subscription, JSON.stringify(data))
|
await webpush.sendNotification(subscription, JSON.stringify(data))
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (err instanceof WebPushError) {
|
if (err instanceof WebPushError) {
|
||||||
if (errorMessages.some((p) => JSON.stringify((err as WebPushError).body).includes(p))) {
|
if (errorMessages.some((p) => JSON.stringify((err as WebPushError).body).includes(p))) {
|
||||||
return 'clear-push'
|
result.push(subscription._id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 'ok'
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export const main = async (): Promise<void> => {
|
export const main = async (): Promise<void> => {
|
||||||
const ses = new SES()
|
const ses = new SES()
|
||||||
console.log('SES service has been started')
|
console.log('SES service has been started')
|
||||||
|
let webpushInitDone = false
|
||||||
|
|
||||||
if (config.PushPublicKey !== undefined && config.PushPrivateKey !== undefined) {
|
if (config.PushPublicKey !== undefined && config.PushPrivateKey !== undefined) {
|
||||||
webpush.setVapidDetails(config.PushSubject ?? 'mailto:hey@huly.io', config.PushPublicKey, config.PushPublicKey)
|
try {
|
||||||
|
const subj = config.PushSubject ?? 'mailto:hey@huly.io'
|
||||||
|
console.log('Setting VAPID details', subj, config.PushPublicKey.length, config.PushPrivateKey.length)
|
||||||
|
webpush.setVapidDetails(config.PushSubject ?? 'mailto:hey@huly.io', config.PushPublicKey, config.PushPrivateKey)
|
||||||
|
webpushInitDone = true
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkAuth = (req: Request<any>, res: Response<any>): boolean => {
|
const checkAuth = (req: Request<any>, res: Response<any>): boolean => {
|
||||||
@ -104,14 +119,18 @@ export const main = async (): Promise<void> => {
|
|||||||
res.status(400).send({ err: "'data' is missing" })
|
res.status(400).send({ err: "'data' is missing" })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const subscription: PushSubscription | undefined = req.body?.subscription
|
const subscriptions: PushSubscription[] | undefined = req.body?.subscriptions
|
||||||
if (subscription === undefined) {
|
if (subscriptions === undefined) {
|
||||||
res.status(400).send({ err: "'subscription' is missing" })
|
res.status(400).send({ err: "'subscriptions' is missing" })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!webpushInitDone) {
|
||||||
|
res.json({ result: [] }).end()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await sendPushToSubscription(subscription, data)
|
const result = await sendPushToSubscription(subscriptions, data)
|
||||||
res.json({ result })
|
res.json({ result }).end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user