fix: email sending and recommendation

* use token in the custom header as the key in rate limiter
This commit is contained in:
Hongbo Wu 2023-06-28 17:00:11 +08:00
parent e0c59434d7
commit b7c133d58e
5 changed files with 39 additions and 37 deletions

View file

@ -2,19 +2,24 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import cors from 'cors'
import express from 'express'
import * as jwt from 'jsonwebtoken'
import { kx } from '../datalayer/knex_config'
import { createPubSubClient } from '../datalayer/pubsub'
import { createPage, getPageByParam, updatePage } from '../elastic/pages'
import { addRecommendation } from '../elastic/recommendation'
import { Recommendation } from '../elastic/types'
import { env } from '../env'
import {
ArticleSavingRequestStatus,
PageType,
UploadFileStatus,
} from '../generated/graphql'
import cors from 'cors'
import { env } from '../env'
import { buildLogger } from '../utils/logger'
import * as jwt from 'jsonwebtoken'
import { corsConfig } from '../utils/corsConfig'
import { Claims } from '../resolvers/types'
import { initModels } from '../server'
import { kx } from '../datalayer/knex_config'
import { getTokenByRequest } from '../utils/auth'
import { corsConfig } from '../utils/corsConfig'
import {
fileNameForFilePath,
generateSlug,
@ -22,15 +27,11 @@ import {
titleForFilePath,
validateUuid,
} from '../utils/helpers'
import { buildLogger } from '../utils/logger'
import {
generateUploadFilePathName,
generateUploadSignedUrl,
} from '../utils/uploads'
import { Claims } from '../resolvers/types'
import { createPage, getPageByParam, updatePage } from '../elastic/pages'
import { createPubSubClient } from '../datalayer/pubsub'
import { Recommendation } from '../elastic/types'
import { addRecommendation } from '../elastic/recommendation'
const logger = buildLogger('app.dispatch')
@ -157,7 +158,7 @@ export function pageRouter() {
'/recommend',
cors<express.Request>(corsConfig),
async (req, res) => {
const token = req?.cookies?.auth || req?.headers?.authorization
const token = getTokenByRequest(req)
if (!token || !jwt.verify(token, env.server.jwtSecret)) {
return res.status(401).send({ errorCode: 'UNAUTHORIZED' })
}

View file

@ -5,7 +5,7 @@ import express from 'express'
import { User } from '../entity/user'
import { getRepository } from '../entity/utils'
import { env } from '../env'
import { getClaimsByToken } from '../utils/auth'
import { getClaimsByToken, getTokenByRequest } from '../utils/auth'
import { corsConfig } from '../utils/corsConfig'
import { buildLogger } from '../utils/logger'
import { sendEmail } from '../utils/sendEmail'
@ -17,10 +17,7 @@ export function userRouter() {
router.post('/email', cors<express.Request>(corsConfig), async (req, res) => {
logger.info('email to-user router')
const token =
req.header('Omnivore-Authorization') ||
req.cookies?.auth ||
req.headers?.authorization
const token = getTokenByRequest(req)
let claims
try {

View file

@ -48,7 +48,7 @@ import { webhooksServiceRouter } from './routers/svc/webhooks'
import { textToSpeechRouter } from './routers/text_to_speech'
import { userRouter } from './routers/user_router'
import { sentryConfig } from './sentry'
import { getClaimsByToken } from './utils/auth'
import { getClaimsByToken, getTokenByRequest } from './utils/auth'
import { corsConfig } from './utils/corsConfig'
import { buildLogger, buildLoggerTransport } from './utils/logger'
@ -102,19 +102,12 @@ export const createApp = (): {
windowMs: 60 * 1000, // 1 minute
max: async (req) => {
// 100 RPM for an authenticated request, 5 for a non-authenticated request
const token = await getClaimsByToken(
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
req.header('authorization') ?? req.cookies['auth']
)
return token ? 100 : 5
const token = getTokenByRequest(req)
const claims = await getClaimsByToken(token)
return claims ? 100 : 5
},
keyGenerator: (req) => {
return (
req.header('authorization') ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
(req.cookies['auth'] as string) ||
req.ip
)
return getTokenByRequest(req) || req.ip
},
// skip preflight requests and test requests
skip: (req) => req.method === 'OPTIONS' || env.dev.isLocal,

View file

@ -1,13 +1,15 @@
import * as bcrypt from 'bcryptjs'
import { v4 as uuidv4 } from 'uuid'
import { Claims, ClaimsToSet } from '../resolvers/types'
import { getRepository } from '../entity/utils'
import { ApiKey } from '../entity/api_key'
import crypto from 'crypto'
import * as jwt from 'jsonwebtoken'
import { env } from '../env'
import express from 'express'
import * as jwt from 'jsonwebtoken'
import { promisify } from 'util'
import { v4 as uuidv4 } from 'uuid'
import { ApiKey } from '../entity/api_key'
import { getRepository } from '../entity/utils'
import { env } from '../env'
import { Claims, ClaimsToSet } from '../resolvers/types'
export const OmnivoreAuthorizationHeader = 'Omnivore-Authorization'
const signToken = promisify(jwt.sign)
@ -112,3 +114,12 @@ export const setAuthInCookie = async (
expires: new Date(new Date().getTime() + 365 * 24 * 60 * 60 * 1000),
})
}
export const getTokenByRequest = (req: express.Request): string | undefined => {
return (
req.header(OmnivoreAuthorizationHeader) ||
req.headers.authorization ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
(req.cookies?.auth as string)
)
}

View file

@ -12,7 +12,7 @@ import {
CreateLabelInput,
} from '../generated/graphql'
import { signFeatureToken } from '../services/features'
import { generateVerificationToken } from './auth'
import { generateVerificationToken, OmnivoreAuthorizationHeader } from './auth'
import { CreateTaskError } from './errors'
import { buildLogger } from './logger'
import View = google.cloud.tasks.v2.Task.View
@ -432,7 +432,7 @@ export const enqueueRecommendation = async (
}
const headers = {
Cookie: `auth=${authToken}`,
[OmnivoreAuthorizationHeader]: authToken,
}
// If there is no Google Cloud Project Id exposed, it means that we are in local environment
if (env.dev.isLocal || !GOOGLE_CLOUD_PROJECT) {