From 4844f9eac21dc47c32eb28e47d0cf475c5e5c176 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Tue, 2 Jul 2024 14:43:46 +0800 Subject: [PATCH 1/4] fix pdf handler sends update-content-job to the wrong redis --- packages/pdf-handler/package.json | 2 +- packages/pdf-handler/src/index.ts | 21 ++++- packages/pdf-handler/src/job.ts | 15 ++-- packages/pdf-handler/src/redis_data_source.ts | 89 ------------------- packages/utils/.eslintignore | 2 + packages/utils/.eslintrc | 6 ++ packages/utils/mocha-config.json | 5 ++ packages/utils/package.json | 25 ++++++ packages/utils/src/index.ts | 1 + packages/utils/src/redis_data_source.ts | 69 ++++++++++++++ packages/utils/test/stub.test.ts | 8 ++ packages/utils/tsconfig.json | 8 ++ 12 files changed, 153 insertions(+), 98 deletions(-) delete mode 100644 packages/pdf-handler/src/redis_data_source.ts create mode 100644 packages/utils/.eslintignore create mode 100644 packages/utils/.eslintrc create mode 100644 packages/utils/mocha-config.json create mode 100644 packages/utils/package.json create mode 100644 packages/utils/src/index.ts create mode 100644 packages/utils/src/redis_data_source.ts create mode 100644 packages/utils/test/stub.test.ts create mode 100644 packages/utils/tsconfig.json diff --git a/packages/pdf-handler/package.json b/packages/pdf-handler/package.json index 01ac1887f..12e20bcc3 100644 --- a/packages/pdf-handler/package.json +++ b/packages/pdf-handler/package.json @@ -30,12 +30,12 @@ "@google-cloud/functions-framework": "3.1.2", "@google-cloud/pubsub": "^4.0.0", "@google-cloud/storage": "^7.0.1", + "@omnivore/utils": "1.0.0", "@sentry/serverless": "^7.77.0", "axios": "^0.27.2", "bullmq": "^5.1.4", "concurrently": "^7.0.0", "dotenv": "^8.2.0", - "ioredis": "^5.3.2", "pdfjs-dist": "^2.9.359" }, "volta": { diff --git a/packages/pdf-handler/src/index.ts b/packages/pdf-handler/src/index.ts index cf3f3359c..7e4ab43c6 100644 --- a/packages/pdf-handler/src/index.ts +++ b/packages/pdf-handler/src/index.ts @@ -1,7 +1,9 @@ import { GetSignedUrlConfig, Storage } from '@google-cloud/storage' +import { RedisDataSource } from '@omnivore/utils' import * as Sentry from '@sentry/serverless' -import { parsePdf } from './pdf' +import 'dotenv/config' import { queueUpdatePageJob, State } from './job' +import { parsePdf } from './pdf' Sentry.GCPFunction.init({ dsn: process.env.SENTRY_DSN, @@ -49,6 +51,7 @@ const getDocumentUrl = async ( } export const updatePageContent = async ( + redisDataSource: RedisDataSource, fileId: string, content?: string, title?: string, @@ -56,7 +59,7 @@ export const updatePageContent = async ( description?: string, state?: State ): Promise => { - const job = await queueUpdatePageJob({ + const job = await queueUpdatePageJob(redisDataSource, { fileId, content, title, @@ -106,6 +109,17 @@ export const pdfHandler = Sentry.GCPFunction.wrapHttpFunction( description, state: State = 'SUCCEEDED' // Default to succeeded even if we fail to parse + const redisDataSource = new RedisDataSource({ + cache: { + url: process.env.REDIS_URL, + cert: process.env.REDIS_CERT, + }, + mq: { + url: process.env.MQ_REDIS_URL, + cert: process.env.MQ_REDIS_CERT, + }, + }) + try { const url = await getDocumentUrl(data) console.log('PDF url: ', url) @@ -130,6 +144,7 @@ export const pdfHandler = Sentry.GCPFunction.wrapHttpFunction( } finally { // Always update the state, even if we fail to parse const result = await updatePageContent( + redisDataSource, data.name, content, title, @@ -147,6 +162,8 @@ export const pdfHandler = Sentry.GCPFunction.wrapHttpFunction( 'state', state ) + + await redisDataSource.shutdown() } } diff --git a/packages/pdf-handler/src/job.ts b/packages/pdf-handler/src/job.ts index ca404aa0f..b9489ebf3 100644 --- a/packages/pdf-handler/src/job.ts +++ b/packages/pdf-handler/src/job.ts @@ -1,13 +1,9 @@ +import { RedisDataSource } from '@omnivore/utils' import { Queue } from 'bullmq' -import { redisDataSource } from './redis_data_source' const QUEUE_NAME = 'omnivore-backend-queue' const JOB_NAME = 'update-pdf-content' -const queue = new Queue(QUEUE_NAME, { - connection: redisDataSource.queueRedisClient, -}) - export type State = 'SUCCEEDED' | 'FAILED' type UpdatePageJobData = { @@ -19,7 +15,14 @@ type UpdatePageJobData = { state?: State } -export const queueUpdatePageJob = async (data: UpdatePageJobData) => { +export const queueUpdatePageJob = async ( + redisDataSource: RedisDataSource, + data: UpdatePageJobData +) => { + const queue = new Queue(QUEUE_NAME, { + connection: redisDataSource.queueRedisClient, + }) + return queue.add(JOB_NAME, data, { priority: 5, attempts: 3, diff --git a/packages/pdf-handler/src/redis_data_source.ts b/packages/pdf-handler/src/redis_data_source.ts deleted file mode 100644 index 1c02c2a47..000000000 --- a/packages/pdf-handler/src/redis_data_source.ts +++ /dev/null @@ -1,89 +0,0 @@ -import Redis, { RedisOptions } from 'ioredis' -import 'dotenv/config' - -export type RedisDataSourceOptions = { - REDIS_URL?: string - REDIS_CERT?: string -} - -export class RedisDataSource { - options: RedisDataSourceOptions - - cacheClient: Redis - queueRedisClient: Redis - - constructor(options: RedisDataSourceOptions) { - this.options = options - - this.cacheClient = createRedisClient('cache', this.options) - this.queueRedisClient = createRedisClient('queue', this.options) - } - - setOptions(options: RedisDataSourceOptions): void { - this.options = options - } - - async shutdown(): Promise { - try { - await this.queueRedisClient?.quit() - await this.cacheClient?.quit() - } catch (err) { - console.error('error while shutting down redis', err) - } - } -} - -const createRedisClient = (name: string, options: RedisDataSourceOptions) => { - const redisURL = options.REDIS_URL - const cert = options.REDIS_CERT?.replace(/\\n/g, '\n') // replace \n with new line - if (!redisURL) { - throw 'Error: no redisURL supplied' - } - - const redisOptions: RedisOptions = { - name, - connectTimeout: 10000, // 10 seconds - tls: cert - ? { - cert, - rejectUnauthorized: false, // for self-signed certs - } - : undefined, - maxRetriesPerRequest: null, - offlineQueue: false, - } - - const redis = new Redis(redisURL, redisOptions) - - redis.on('connect', () => { - console.log('Redis connected', name) - }) - - redis.on('error', (err) => { - console.error('Redis error', err, name) - }) - - redis.on('close', () => { - console.log('Redis closed', name) - }) - - return redis -} - -export const redisDataSource = new RedisDataSource({ - REDIS_URL: process.env.REDIS_URL, - REDIS_CERT: process.env.REDIS_CERT, -}) - -// eslint-disable-next-line @typescript-eslint/no-misused-promises -process.on('SIGINT', async () => { - console.log('SIGINT signal received.') - - try { - await redisDataSource.shutdown() - } catch (error) { - console.error('error while shutting down redis', error) - } - - process.exit(0) -}) diff --git a/packages/utils/.eslintignore b/packages/utils/.eslintignore new file mode 100644 index 000000000..b38db2f29 --- /dev/null +++ b/packages/utils/.eslintignore @@ -0,0 +1,2 @@ +node_modules/ +build/ diff --git a/packages/utils/.eslintrc b/packages/utils/.eslintrc new file mode 100644 index 000000000..644bb1aec --- /dev/null +++ b/packages/utils/.eslintrc @@ -0,0 +1,6 @@ +{ + "extends": "../../.eslintrc", + "parserOptions": { + "project": "tsconfig.json" + } +} diff --git a/packages/utils/mocha-config.json b/packages/utils/mocha-config.json new file mode 100644 index 000000000..8e24eb08b --- /dev/null +++ b/packages/utils/mocha-config.json @@ -0,0 +1,5 @@ +{ + "extension": ["ts"], + "spec": "test/**/*.test.ts", + "timeout": 10000 + } diff --git a/packages/utils/package.json b/packages/utils/package.json new file mode 100644 index 000000000..46cfe0cca --- /dev/null +++ b/packages/utils/package.json @@ -0,0 +1,25 @@ +{ + "name": "@omnivore/utils", + "version": "1.0.0", + "description": "Utility functions for Omnivore packages.", + "main": "./build/src/index.js", + "types": "./build/src/index.d.ts", + "scripts": { + "test": "yarn mocha -r ts-node/register --config mocha-config.json", + "lint": "eslint src --ext ts,js,tsx,jsx", + "build": "tsc" + }, + "devDependencies": { + "@types/chai": "^4.3.6", + "@types/mocha": "^10.0.0", + "chai": "^4.3.6", + "eslint-plugin-prettier": "^4.0.0", + "mocha": "^10.0.0" + }, + "dependencies": { + "ioredis": "^5.3.2" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts new file mode 100644 index 000000000..72c968fd3 --- /dev/null +++ b/packages/utils/src/index.ts @@ -0,0 +1 @@ +export * from './redis_data_source' diff --git a/packages/utils/src/redis_data_source.ts b/packages/utils/src/redis_data_source.ts new file mode 100644 index 000000000..3c982e90f --- /dev/null +++ b/packages/utils/src/redis_data_source.ts @@ -0,0 +1,69 @@ +import Redis, { RedisOptions } from 'ioredis' + +type RedisClientType = 'cache' | 'mq' +type RedisDataSourceOption = { + url?: string + cert?: string +} +export type RedisDataSourceOptions = { + [key in RedisClientType]: RedisDataSourceOption +} + +export class RedisDataSource { + options: RedisDataSourceOptions + + cacheClient: Redis + queueRedisClient: Redis + + constructor(options: RedisDataSourceOptions) { + this.options = options + + const cacheClient = createIORedisClient('cache', this.options) + if (!cacheClient) throw 'Error initializing cache redis client' + + this.cacheClient = cacheClient + this.queueRedisClient = + createIORedisClient('mq', this.options) || this.cacheClient // if mq is not defined, use cache + } + + async shutdown(): Promise { + try { + await this.queueRedisClient?.quit() + await this.cacheClient?.quit() + + console.log('redis shutdown complete') + } catch (err) { + console.error('error while shutting down redis', err) + } + } +} + +const createIORedisClient = ( + name: RedisClientType, + options: RedisDataSourceOptions +): Redis | undefined => { + const option = options[name] + const redisURL = option.url + if (!redisURL) { + console.log(`no redisURL supplied: ${name}`) + return undefined + } + + const redisCert = option.cert + const tls = + redisURL.startsWith('rediss://') && redisCert + ? { + ca: redisCert, + rejectUnauthorized: false, + } + : undefined + + const redisOptions: RedisOptions = { + tls, + name, + connectTimeout: 10000, + maxRetriesPerRequest: null, + offlineQueue: false, + } + return new Redis(redisURL, redisOptions) +} diff --git a/packages/utils/test/stub.test.ts b/packages/utils/test/stub.test.ts new file mode 100644 index 000000000..24ad25c8f --- /dev/null +++ b/packages/utils/test/stub.test.ts @@ -0,0 +1,8 @@ +import 'mocha' +import { expect } from 'chai' + +describe('stub test', () => { + it('should pass', () => { + expect(true).to.be.true + }) +}) diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json new file mode 100644 index 000000000..fe4d0245b --- /dev/null +++ b/packages/utils/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "./../../tsconfig.json", + "compilerOptions": { + "declaration": true, + "outDir": "build", + }, + "include": ["src", "test"] +} From 0d1583351121a905b0733a7d7318f4edea0ca90f Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Tue, 2 Jul 2024 14:59:42 +0800 Subject: [PATCH 2/4] update inbound-email-handler to use @omnivore/utils to create redis connection --- packages/inbound-email-handler/package.json | 2 +- .../inbound-email-handler/src/attachment.ts | 4 +- packages/inbound-email-handler/src/index.ts | 38 +++++-- packages/inbound-email-handler/src/job.ts | 11 ++- .../inbound-email-handler/src/newsletter.ts | 4 +- .../src/redis_data_source.ts | 99 ------------------- 6 files changed, 45 insertions(+), 113 deletions(-) delete mode 100644 packages/inbound-email-handler/src/redis_data_source.ts diff --git a/packages/inbound-email-handler/package.json b/packages/inbound-email-handler/package.json index 2581a61a2..0839a709b 100644 --- a/packages/inbound-email-handler/package.json +++ b/packages/inbound-email-handler/package.json @@ -35,11 +35,11 @@ "dependencies": { "@google-cloud/functions-framework": "3.1.2", "@google-cloud/storage": "^7.0.1", + "@omnivore/utils": "1.0.0", "@sentry/serverless": "^7.77.0", "addressparser": "^1.0.1", "bullmq": "^5.1.1", "dotenv": "^8.2.0", - "ioredis": "^5.3.2", "parse-headers": "^2.0.4", "parse-multipart-data": "^1.2.1", "rfc2047": "^4.0.1", diff --git a/packages/inbound-email-handler/src/attachment.ts b/packages/inbound-email-handler/src/attachment.ts index db3d0d094..30b3833e7 100644 --- a/packages/inbound-email-handler/src/attachment.ts +++ b/packages/inbound-email-handler/src/attachment.ts @@ -1,4 +1,5 @@ import { Storage } from '@google-cloud/storage' +import { RedisDataSource } from '@omnivore/utils' import { v4 as uuid } from 'uuid' import { EmailJobType, queueEmailJob } from './job' @@ -37,6 +38,7 @@ export const uploadToBucket = async ( } export const handleAttachments = async ( + redisDataSource: RedisDataSource, from: string, to: string, subject: string, @@ -54,7 +56,7 @@ export const handleAttachments = async ( public: false, }) - await queueEmailJob(EmailJobType.SaveAttachment, { + await queueEmailJob(redisDataSource, EmailJobType.SaveAttachment, { from, to, uploadFile: { diff --git a/packages/inbound-email-handler/src/index.ts b/packages/inbound-email-handler/src/index.ts index 0abd80dac..8d3232109 100644 --- a/packages/inbound-email-handler/src/index.ts +++ b/packages/inbound-email-handler/src/index.ts @@ -2,7 +2,9 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unused-vars */ +import { RedisDataSource } from '@omnivore/utils' import * as Sentry from '@sentry/serverless' +import 'dotenv/config' import parseHeaders from 'parse-headers' import * as multipart from 'parse-multipart-data' import rfc2047 from 'rfc2047' @@ -72,6 +74,17 @@ export const inboundEmailHandler = Sentry.GCPFunction.wrapHttpFunction( ? parseUnsubscribe(unSubHeader) : undefined + const redisDataSource = new RedisDataSource({ + cache: { + url: process.env.REDIS_URL, + cert: process.env.REDIS_CERT, + }, + mq: { + url: process.env.MQ_REDIS_URL, + cert: process.env.MQ_REDIS_CERT, + }, + }) + try { // check if it is a subscription or google confirmation email const isGoogleConfirmation = isGoogleConfirmationEmail(from, subject) @@ -79,11 +92,16 @@ export const inboundEmailHandler = Sentry.GCPFunction.wrapHttpFunction( console.debug('handleConfirmation', from, subject) // we need to parse the confirmation code from the email if (isGoogleConfirmation) { - await handleGoogleConfirmationEmail(from, to, subject) + await handleGoogleConfirmationEmail( + redisDataSource, + from, + to, + subject + ) } // forward emails - await queueEmailJob(EmailJobType.ForwardEmail, { + await queueEmailJob(redisDataSource, EmailJobType.ForwardEmail, { from, to, subject, @@ -98,13 +116,19 @@ export const inboundEmailHandler = Sentry.GCPFunction.wrapHttpFunction( if (attachments.length > 0) { console.debug('handle attachments', from, to, subject) // save the attachments as articles - await handleAttachments(from, to, subject, attachments) + await handleAttachments( + redisDataSource, + from, + to, + subject, + attachments + ) return res.send('ok') } // all other emails are considered newsletters // queue newsletter emails - await queueEmailJob(EmailJobType.SaveNewsletter, { + await queueEmailJob(redisDataSource, EmailJobType.SaveNewsletter, { from, to, subject, @@ -128,7 +152,7 @@ export const inboundEmailHandler = Sentry.GCPFunction.wrapHttpFunction( ) // fallback to forward the email - await queueEmailJob(EmailJobType.ForwardEmail, { + await queueEmailJob(redisDataSource, EmailJobType.ForwardEmail, { from, to, subject, @@ -139,7 +163,9 @@ export const inboundEmailHandler = Sentry.GCPFunction.wrapHttpFunction( replyTo, }) - res.send('ok') + return res.send('ok') + } finally { + await redisDataSource.shutdown() } } catch (e) { console.error(e) diff --git a/packages/inbound-email-handler/src/job.ts b/packages/inbound-email-handler/src/job.ts index 89a2fea40..2d379daf5 100644 --- a/packages/inbound-email-handler/src/job.ts +++ b/packages/inbound-email-handler/src/job.ts @@ -1,5 +1,5 @@ +import { RedisDataSource } from '@omnivore/utils' import { BulkJobOptions, Queue } from 'bullmq' -import { redisDataSource } from './redis_data_source' const QUEUE_NAME = 'omnivore-backend-queue' export enum EmailJobType { @@ -28,10 +28,6 @@ interface EmailJobData { confirmationCode?: string } -const queue = new Queue(QUEUE_NAME, { - connection: redisDataSource.queueRedisClient, -}) - const getPriority = (jobType: EmailJobType): number => { // we want to prioritized jobs by the expected time to complete // lower number means higher priority @@ -65,8 +61,13 @@ const getOpts = (jobType: EmailJobType): BulkJobOptions => { } export const queueEmailJob = async ( + redisDataSource: RedisDataSource, jobType: EmailJobType, data: EmailJobData ) => { + const queue = new Queue(QUEUE_NAME, { + connection: redisDataSource.queueRedisClient, + }) + await queue.add(jobType, data, getOpts(jobType)) } diff --git a/packages/inbound-email-handler/src/newsletter.ts b/packages/inbound-email-handler/src/newsletter.ts index b31103ada..f72ee2c00 100644 --- a/packages/inbound-email-handler/src/newsletter.ts +++ b/packages/inbound-email-handler/src/newsletter.ts @@ -1,3 +1,4 @@ +import { RedisDataSource } from '@omnivore/utils' import addressparser from 'addressparser' import { EmailJobType, queueEmailJob } from './job' @@ -40,6 +41,7 @@ export const parseAuthor = (address: string): string => { } export const handleGoogleConfirmationEmail = async ( + redisDataSource: RedisDataSource, from: string, to: string, subject: string @@ -58,7 +60,7 @@ export const handleGoogleConfirmationEmail = async ( } const message = { from, to, confirmationCode, subject } - return queueEmailJob(EmailJobType.ConfirmationEmail, message) + return queueEmailJob(redisDataSource, EmailJobType.ConfirmationEmail, message) } export const getConfirmationCode = (subject: string): string | undefined => { diff --git a/packages/inbound-email-handler/src/redis_data_source.ts b/packages/inbound-email-handler/src/redis_data_source.ts deleted file mode 100644 index 05bf8d26c..000000000 --- a/packages/inbound-email-handler/src/redis_data_source.ts +++ /dev/null @@ -1,99 +0,0 @@ -import Redis, { RedisOptions } from 'ioredis' -import 'dotenv/config' - -type RedisClientType = 'cache' | 'mq' -type RedisDataSourceOption = { - url?: string - cert?: string -} -export type RedisDataSourceOptions = { - [key in RedisClientType]: RedisDataSourceOption -} - -export class RedisDataSource { - options: RedisDataSourceOptions - - cacheClient: Redis - queueRedisClient: Redis - - constructor(options: RedisDataSourceOptions) { - this.options = options - - const cacheClient = createIORedisClient('cache', this.options) - if (!cacheClient) throw 'Error initializing cache redis client' - - this.cacheClient = cacheClient - this.queueRedisClient = - createIORedisClient('mq', this.options) || this.cacheClient // if mq is not defined, use cache - } - - async shutdown(): Promise { - try { - await this.queueRedisClient?.quit() - await this.cacheClient?.quit() - } catch (err) { - console.error('error while shutting down redis', err) - } - } -} - -const createIORedisClient = ( - name: RedisClientType, - options: RedisDataSourceOptions -): Redis | undefined => { - const option = options[name] - const redisURL = option.url - if (!redisURL) { - console.log(`no redisURL supplied: ${name}`) - return undefined - } - - const redisCert = option.cert - const tls = - redisURL.startsWith('rediss://') && redisCert - ? { - ca: redisCert, - rejectUnauthorized: false, - } - : undefined - - const redisOptions: RedisOptions = { - tls, - name, - connectTimeout: 10000, - maxRetriesPerRequest: null, - offlineQueue: false, - } - return new Redis(redisURL, redisOptions) -} - -export const redisDataSource = new RedisDataSource({ - cache: { - url: process.env.REDIS_URL, - cert: process.env.REDIS_CERT, - }, - mq: { - url: process.env.MQ_REDIS_URL, - cert: process.env.MQ_REDIS_CERT, - }, -}) - -const gracefulShutdown = async (signal: string) => { - console.log(`Received ${signal}, shutting down gracefully...`) - - await redisDataSource.shutdown() - console.log('redis shutdown successfully') - - process.exit(0) -} - -process.on('SIGINT', () => { - ;(async () => { - await gracefulShutdown('SIGINT') - })() -}) -process.on('SIGTERM', () => { - ;(async () => { - await gracefulShutdown('SIGTERM') - })() -}) From edefa988e79f1472ae550250ec13146633d7d6d2 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Tue, 2 Jul 2024 15:00:51 +0800 Subject: [PATCH 3/4] fix tests --- packages/inbound-email-handler/mocha-config.json | 1 - packages/inbound-email-handler/test/global-teardown.ts | 5 ----- 2 files changed, 6 deletions(-) delete mode 100644 packages/inbound-email-handler/test/global-teardown.ts diff --git a/packages/inbound-email-handler/mocha-config.json b/packages/inbound-email-handler/mocha-config.json index 897a3bb1e..8e24eb08b 100644 --- a/packages/inbound-email-handler/mocha-config.json +++ b/packages/inbound-email-handler/mocha-config.json @@ -1,6 +1,5 @@ { "extension": ["ts"], "spec": "test/**/*.test.ts", - "require": ["test/global-teardown.ts"], "timeout": 10000 } diff --git a/packages/inbound-email-handler/test/global-teardown.ts b/packages/inbound-email-handler/test/global-teardown.ts deleted file mode 100644 index cb733d056..000000000 --- a/packages/inbound-email-handler/test/global-teardown.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { redisDataSource } from '../src/redis_data_source' - -export const mochaGlobalTeardown = async () => { - await redisDataSource.shutdown() -} From b1ba2d3f26ce6aef91ef99d5480186d431d22522 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Tue, 2 Jul 2024 15:25:51 +0800 Subject: [PATCH 4/4] fix Dockerfile --- packages/inbound-email-handler/Dockerfile | 3 +++ packages/pdf-handler/Dockerfile | 3 +++ 2 files changed, 6 insertions(+) diff --git a/packages/inbound-email-handler/Dockerfile b/packages/inbound-email-handler/Dockerfile index f7bfa1cfa..dfb9b4672 100644 --- a/packages/inbound-email-handler/Dockerfile +++ b/packages/inbound-email-handler/Dockerfile @@ -11,11 +11,14 @@ COPY .eslintrc . COPY /packages/inbound-email-handler/package.json ./packages/inbound-email-handler/package.json COPY /packages/content-handler/package.json ./packages/content-handler/package.json +COPY /packages/utils/package.json ./packages/utils/package.json RUN yarn install --pure-lockfile ADD /packages/inbound-email-handler ./packages/inbound-email-handler ADD /packages/content-handler ./packages/content-handler +ADD /packages/utils ./packages/utils +RUN yarn workspace @omnivore/utils build RUN yarn workspace @omnivore/content-handler build RUN yarn workspace @omnivore/inbound-email-handler build diff --git a/packages/pdf-handler/Dockerfile b/packages/pdf-handler/Dockerfile index 5cbdb9214..e3d44404c 100644 --- a/packages/pdf-handler/Dockerfile +++ b/packages/pdf-handler/Dockerfile @@ -10,10 +10,13 @@ COPY .prettierrc . COPY .eslintrc . COPY /packages/pdf-handler/package.json ./packages/pdf-handler/package.json +COPY /packages/utils/package.json ./packages/utils/package.json RUN yarn install --pure-lockfile ADD /packages/pdf-handler ./packages/pdf-handler +ADD /packages/utils ./packages/utils +RUN yarn workspace @omnivore/utils build RUN yarn workspace @omnivore/pdf-handler build # After building, fetch the production dependencies