Call api to get rules and device tokens in rules handler
This commit is contained in:
@ -159,11 +159,6 @@ services:
|
||||
- "9091:8080"
|
||||
environment:
|
||||
- PUBSUB_VERIFICATION_TOKEN=some_token
|
||||
- PG_HOST=postgres
|
||||
- PG_PORT=5432
|
||||
- PG_USER=app_user
|
||||
- PG_PASSWORD=app_pass
|
||||
- PG_DB=omnivore
|
||||
depends_on:
|
||||
migrate:
|
||||
condition: service_completed_successfully
|
||||
|
||||
@ -21,11 +21,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@google-cloud/functions-framework": "3.1.2",
|
||||
"@sentry/serverless": "^6.16.1",
|
||||
"axios": "^0.27.2",
|
||||
"dotenv": "^16.0.1",
|
||||
"firebase-admin": "^10.0.2",
|
||||
"@sentry/serverless": "^6.16.1",
|
||||
"pg": "^8.3.3",
|
||||
"typeorm": "^0.3.4",
|
||||
"typeorm-naming-strategies": "^4.1.0"
|
||||
"jsonwebtoken": "^8.5.1"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import * as Sentry from '@sentry/serverless'
|
||||
import express, { Request, Response } from 'express'
|
||||
import * as dotenv from 'dotenv'
|
||||
import { closeDBConnection, createDBConnection, getRepository } from './db'
|
||||
import { Rules } from './entity/rules'
|
||||
import { triggerActions } from './rule' // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
|
||||
import { getEnabledRules, triggerActions } from './rule'
|
||||
import { promisify } from 'util'
|
||||
import * as jwt from 'jsonwebtoken'
|
||||
|
||||
const signToken = promisify(jwt.sign)
|
||||
|
||||
dotenv.config()
|
||||
|
||||
@ -58,8 +60,22 @@ const readPushSubscription = (
|
||||
return { message: message, expired: expired(body) }
|
||||
}
|
||||
|
||||
export const getAuthToken = async (
|
||||
userId: string,
|
||||
jwtSecret: string
|
||||
): Promise<string> => {
|
||||
const auth = await signToken({ uid: userId }, jwtSecret)
|
||||
return auth as string
|
||||
}
|
||||
|
||||
export const ruleHandler = Sentry.GCPFunction.wrapHttpFunction(
|
||||
async (req: Request, res: Response) => {
|
||||
const apiEndpoint = process.env.API_ENDPOINT
|
||||
const jwtSecret = process.env.JWT_SECRET
|
||||
if (!apiEndpoint || !jwtSecret) {
|
||||
throw new Error('REST_BACKEND_ENDPOINT or JWT_SECRET not set')
|
||||
}
|
||||
|
||||
const { message: msgStr, expired } = readPushSubscription(req)
|
||||
|
||||
if (!msgStr) {
|
||||
@ -88,16 +104,10 @@ export const ruleHandler = Sentry.GCPFunction.wrapHttpFunction(
|
||||
return
|
||||
}
|
||||
|
||||
await createDBConnection()
|
||||
// get rules by calling api
|
||||
const rules = await getEnabledRules(userId, apiEndpoint, jwtSecret)
|
||||
|
||||
const rules = await getRepository(Rules).findBy({
|
||||
user: { id: userId },
|
||||
enabled: true,
|
||||
})
|
||||
|
||||
await triggerActions(userId, rules, data)
|
||||
|
||||
await closeDBConnection()
|
||||
await triggerActions(userId, rules, data, apiEndpoint, jwtSecret)
|
||||
|
||||
res.status(200).send('OK')
|
||||
} catch (error) {
|
||||
|
||||
@ -1,23 +1,80 @@
|
||||
import { Rules } from './entity/rules'
|
||||
import {
|
||||
getBatchMessages,
|
||||
getUserDeviceTokens,
|
||||
sendBatchPushNotifications,
|
||||
} from './sendNotification'
|
||||
import { getRepository } from './db'
|
||||
import { UserDeviceToken } from './entity/user_device_tokens'
|
||||
import { PubSubData } from './index'
|
||||
import { getAuthToken, PubSubData } from './index'
|
||||
import axios from 'axios'
|
||||
|
||||
enum RuleActionType {
|
||||
export enum RuleActionType {
|
||||
AddLabel = 'ADD_LABEL',
|
||||
Archive = 'ARCHIVE',
|
||||
MarkAsRead = 'MARK_AS_READ',
|
||||
SendNotification = 'SEND_NOTIFICATION',
|
||||
}
|
||||
|
||||
export interface RuleAction {
|
||||
type: RuleActionType
|
||||
params: string[]
|
||||
}
|
||||
|
||||
export interface Rule {
|
||||
id: string
|
||||
userId: string
|
||||
name: string
|
||||
filter: string
|
||||
actions: RuleAction[]
|
||||
description?: string
|
||||
enabled: boolean
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
export const getEnabledRules = async (
|
||||
userId: string,
|
||||
apiEndpoint: string,
|
||||
jwtSecret: string
|
||||
): Promise<Rule[]> => {
|
||||
const auth = await getAuthToken(userId, jwtSecret)
|
||||
|
||||
const data = JSON.stringify({
|
||||
query: `query {
|
||||
rules(enabled: true) {
|
||||
... on RulesError {
|
||||
errorCodes
|
||||
}
|
||||
... on RulesSuccess {
|
||||
rules {
|
||||
id
|
||||
name
|
||||
filter
|
||||
actions {
|
||||
type
|
||||
params
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`,
|
||||
})
|
||||
|
||||
const response = await axios.post(`${apiEndpoint}/graphql`, data, {
|
||||
headers: {
|
||||
Cookie: `auth=${auth};`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
return response.data.data.rules.rules as Rule[]
|
||||
}
|
||||
|
||||
export const triggerActions = async (
|
||||
userId: string,
|
||||
rules: Rules[],
|
||||
data: PubSubData
|
||||
rules: Rule[],
|
||||
data: PubSubData,
|
||||
apiEndpoint: string,
|
||||
jwtSecret: string
|
||||
) => {
|
||||
for (const rule of rules) {
|
||||
// TODO: filter out rules that don't match the trigger
|
||||
@ -37,16 +94,20 @@ export const triggerActions = async (
|
||||
console.log('No notification messages provided')
|
||||
continue
|
||||
}
|
||||
await sendNotification(userId, action.params)
|
||||
await sendNotification(userId, action.params, apiEndpoint, jwtSecret)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const sendNotification = async (userId: string, messages: string[]) => {
|
||||
const tokens = await getRepository(UserDeviceToken).findBy({
|
||||
user: { id: userId },
|
||||
})
|
||||
export const sendNotification = async (
|
||||
userId: string,
|
||||
messages: string[],
|
||||
apiEndpoint: string,
|
||||
jwtSecret: string
|
||||
) => {
|
||||
// get device tokens by calling api
|
||||
const tokens = await getUserDeviceTokens(userId, apiEndpoint, jwtSecret)
|
||||
|
||||
const batchMessages = getBatchMessages(
|
||||
messages,
|
||||
|
||||
@ -5,12 +5,58 @@ import {
|
||||
Message,
|
||||
MulticastMessage,
|
||||
} from 'firebase-admin/messaging'
|
||||
import axios from 'axios'
|
||||
import { getAuthToken } from './index'
|
||||
|
||||
export interface UserDeviceToken {
|
||||
id: string
|
||||
token: string
|
||||
userId: string
|
||||
createdAt: Date
|
||||
}
|
||||
|
||||
// getting credentials from App Engine
|
||||
initializeApp({
|
||||
credential: applicationDefault(),
|
||||
})
|
||||
|
||||
export const getUserDeviceTokens = async (
|
||||
userId: string,
|
||||
apiEndpoint: string,
|
||||
jwtSecret: string
|
||||
): Promise<UserDeviceToken[]> => {
|
||||
const auth = await getAuthToken(userId, jwtSecret)
|
||||
|
||||
const data = JSON.stringify({
|
||||
query: `query {
|
||||
userDeviceTokens(userId: "${userId}") {
|
||||
... on UserDeviceTokensError {
|
||||
errorCodes
|
||||
}
|
||||
... on UserDeviceTokensSuccess {
|
||||
userDeviceTokens {
|
||||
id
|
||||
token
|
||||
userId
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}`,
|
||||
})
|
||||
|
||||
const response = await axios.post(`${apiEndpoint}/graphql`, data, {
|
||||
headers: {
|
||||
Cookie: `auth=${auth};`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
return response.data.data.userDeviceTokens
|
||||
.userDeviceTokens as UserDeviceToken[]
|
||||
}
|
||||
|
||||
export const getBatchMessages = (
|
||||
messages: string[],
|
||||
tokens: string[]
|
||||
|
||||
Reference in New Issue
Block a user