├── .gitignore ├── .jshintrc ├── LICENSE ├── README.md ├── buildspec.yml ├── docs ├── .nojekyll ├── _coverpage.md ├── _sidebar.md ├── content │ ├── _sidebar.md │ ├── api │ │ ├── _sidebar.md │ │ ├── components.md │ │ └── paths.md │ ├── api_reference.md │ ├── concepts │ │ └── _sidebar.md │ ├── quick_start.md │ ├── release_notes.md │ └── releases │ │ ├── 1.0.5.md │ │ ├── 1.1.0.md │ │ ├── 1.1.1.md │ │ ├── 2.0.0.md │ │ ├── 2.1.0.md │ │ ├── 2.1.1.md │ │ ├── 3.0.0.md │ │ ├── 3.0.1.md │ │ └── _sidebar.md ├── index.html ├── static │ └── openapi.yml └── styles │ └── override.css ├── lerna.json ├── openapi.yml ├── package.json ├── packages └── api │ ├── Makefile │ ├── README.md │ ├── common │ ├── api │ │ └── PrinterFailureTypes.js │ └── aws │ │ └── LambdaHelper.js │ ├── gulpfile.js │ ├── log4js.config.js │ ├── package.json │ ├── print │ ├── print.js │ └── test │ │ ├── print.5MB.json │ │ └── print.json │ ├── serverless.yml │ ├── service │ ├── test │ │ └── version.json │ └── version.js │ └── webpack.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .serverless 3 | .webpack 4 | node_modules 5 | out.pdf 6 | 7 | chrome-aws-lambda 8 | 9 | layers 10 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esversion": 9, 3 | "validthis": true, 4 | "-W018": true 5 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020, Barchart.com, Inc., http://www.barchart.com/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/barchart/barchart-common-js 6 | 7 | The following license applies to all parts of this software. 8 | 9 | ==== 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining 12 | a copy of this software and associated documentation files (the 13 | "Software"), to deal in the Software without restriction, including 14 | without limitation the rights to use, copy, modify, merge, publish, 15 | distribute, sublicense, and/or sell copies of the Software, and to 16 | permit persons to whom the Software is furnished to do so, subject to 17 | the following conditions: 18 | 19 | The above copyright notice and this permission notice shall be 20 | included in all copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 25 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 26 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 27 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 28 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @barchart/aws-lambda-pdf-generator 2 | 3 | [![AWS CodeBuild](https://codebuild.us-east-1.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiTmtsZEw3M2l3cktxd3crQTJpMVVRbEUzU1dOMFBodFU0MlhaNUFZaC9kVzBIN1FYUXVVZFFUK29vcU5tazJyckNtVFFxL3BoSEdYMEk3V3dUOEhNUFhNPSIsIml2UGFyYW1ldGVyU3BlYyI6Ik84YnJJU1NUZDMvR3VLaXYiLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D&branch=master)](https://github.com/barchart/aws-lambda-pdf-generator) 4 | [![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lerna.js.org/) 5 | 6 | **Generate PDF files from HTML documents.** 7 | 8 | _Simply ```POST``` an HTML document to a web service and receive an ```application/pdf``` document in response._ 9 | 10 | ### Implementation 11 | 12 | * The [Serverless](https://www.serverless.com/) framework creates a web service using [AWS API Gateway](https://aws.amazon.com/api-gateway/) and [AWS Lambda](https://aws.amazon.com/lambda/). 13 | * The web service uses [Puppeteer](https://github.com/puppeteer/puppeteer) and [Chromium](https://www.chromium.org/) to render an HTML document as a PDF file. 14 | 15 | ### Documentation 16 | 17 | Complete documentation for installation and operation can be found [here](https://barchart.github.io/aws-lambda-pdf-generator/#/). 18 | 19 | ### Technical Notes 20 | 21 | **ARM Architecture (2023/08/31, v3.0.0)** 22 | 23 | > It would be nice to use ARM architecture for AWS Lambda functions (and use Apple silicon locally). However, the [`@sparticuz/chromium`](https://github.com/Sparticuz/chromium) project has not yet added support. See the comments [here](https://github.com/Sparticuz/chromium#running-locally--headlessheadful-mode). 24 | 25 | ### License 26 | 27 | This software is provided under the MIT license. 28 | -------------------------------------------------------------------------------- /buildspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | phases: 4 | install: 5 | runtime-versions: 6 | nodejs: 20.x 7 | 8 | pre_build: 9 | commands: 10 | - npm install yarn --location=global 11 | - yarn install 12 | 13 | build: 14 | commands: 15 | - yarn lint 16 | - cd packages/api 17 | - make all 18 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/barchart/aws-lambda-pdf-generator/646117963b2ecc42543c6bc4560d1854b3f0173a/docs/.nojekyll -------------------------------------------------------------------------------- /docs/_coverpage.md: -------------------------------------------------------------------------------- 1 | # @barchart/aws-lambda-pdf-generator 3.0.1 2 | 3 | > Serverless application that coverts HTML to PDF 4 | 5 | [Quick Start](/content/quick_start) 6 | -------------------------------------------------------------------------------- /docs/_sidebar.md: -------------------------------------------------------------------------------- 1 | * [Quick Start](/content/quick_start) 2 | 3 | * [API Reference](/content/api_reference) 4 | 5 | * [Release Notes](/content/release_notes) 6 | -------------------------------------------------------------------------------- /docs/content/_sidebar.md: -------------------------------------------------------------------------------- 1 | * [Quick Start](/content/quick_start) 2 | 3 | * [API Reference](/content/api_reference) 4 | 5 | * [Release Notes](/content/release_notes) 6 | -------------------------------------------------------------------------------- /docs/content/api/_sidebar.md: -------------------------------------------------------------------------------- 1 | * [Quick Start](/content/quick_start) 2 | 3 | * [API Reference](/content/api_reference) 4 | * [Components](/content/api/components) 5 | * [Paths](/content/api/paths) 6 | 7 | * [Release Notes](/content/release_notes) 8 | -------------------------------------------------------------------------------- /docs/content/api/components.md: -------------------------------------------------------------------------------- 1 | # Components 2 | 3 | ## Responses 4 | 5 | ### Success :id=responsessuccess 6 | > Generated PDF file. 7 | 8 | 9 | * * * 10 | 11 | ### BadRequest :id=responsesbadrequest 12 | > Bad request. 13 | 14 | **Content Type**: application/json 15 | 16 | **Response Type:** Array<Object> 17 | 18 | | Name | Type | Required | Nullable | Description | 19 | | ---- | ---- | -------- | -------- | ----------- | 20 | | value | Object | false | false | | 21 | | value.code | String | false | false | | 22 | | value.message | String | false | false | | 23 | | children | Array | false | false | | 24 | 25 | **Example**: 26 | 27 | ```json 28 | [ 29 | { 30 | "value": { 31 | "code": "PRINT_FAILED_NO_HTML_LAYOUT", 32 | "message": "Failed to print PDF, missing html layout." 33 | }, 34 | "children": [] 35 | } 36 | ] 37 | ``` 38 | 39 | * * * 40 | 41 | ### ServerError :id=responsesservererror 42 | > Server error. 43 | 44 | **Content Type**: application/json 45 | 46 | **Response Type:** Array<Object> 47 | 48 | | Name | Type | Required | Nullable | Description | 49 | | ---- | ---- | -------- | -------- | ----------- | 50 | | value | Object | false | false | | 51 | | value.code | String | false | false | | 52 | | value.message | String | false | false | | 53 | | children | Array | false | false | | 54 | 55 | **Example**: 56 | 57 | ```json 58 | [ 59 | { 60 | "value": { 61 | "code": "REQUEST_GENERAL_FAILURE", 62 | "message": "An attempt to print html page failed for unspecified reason(s)." 63 | }, 64 | "children": [] 65 | } 66 | ] 67 | ``` 68 | 69 | * * * 70 | 71 | 72 | -------------------------------------------------------------------------------- /docs/content/api/paths.md: -------------------------------------------------------------------------------- 1 | # Paths 2 | 3 | ## POST /print 4 | 5 | > Prints PDF page from html layout (chrome v92 by default). 6 | 7 | **Summary**: Prints PDF page from html layout (chrome v92 by default). 8 | 9 | #### Request Body 10 | 11 | **Content Type**: application/json 12 | 13 | **Type**: Object 14 | 15 | | Name | Type | Required | Nullable | Description | 16 | | ---- | ---- | -------- | -------- | ----------- | 17 | | source | String | false | false | Optional string to define the source of request | 18 | | html | String | true | false | HTML layout to print | 19 | | settings | Object | false | false | Optional [settings](https://github.com/puppeteer/puppeteer/blob/v5.2.1/docs/api.md#pagepdfoptions) passed to page.pdf() | 20 | 21 | **Example**: 22 | 23 | ```json 24 | { 25 | "source": "crude-oil-price-report", 26 | "html": "test", 27 | "settings": { 28 | "width": "100px" 29 | } 30 | } 31 | ``` 32 | 33 | #### Responses 34 | 35 | **Status Code**: 200 - [Success](/content/api/components?id=responsessuccess) 36 | 37 | * * * 38 | 39 | **Status Code**: 400 - [BadRequest](/content/api/components?id=responsesbadrequest) 40 | 41 | * * * 42 | 43 | **Status Code**: 500 - [ServerError](/content/api/components?id=responsesservererror) 44 | 45 | * * * 46 | 47 | -------------------------------------------------------------------------------- /docs/content/api_reference.md: -------------------------------------------------------------------------------- 1 | # API Reference 2 | 3 | ## PDF generator API 2.1.0 {docsify-ignore} 4 | 5 | > PDF generator API 6 | 7 | ## OpenAPI Definition {docsify-ignore} 8 | 9 | [Download](static/openapi.yml) 10 | 11 | ## Contents {docsify-ignore} 12 | 13 | * [Components](#Components) 14 | * [Paths](#Paths) 15 | 16 | 17 | ## Components {docsify-ignore} 18 | 19 | ### Responses 20 | 21 | * [Success](/content/api/components?id=responsesSuccess) 22 | * [BadRequest](/content/api/components?id=responsesBadRequest) 23 | * [ServerError](/content/api/components?id=responsesServerError) 24 | 25 | 26 | ## Paths {docsify-ignore} 27 | 28 | * [POST /print](/content/api/paths?id=post-print) 29 | -------------------------------------------------------------------------------- /docs/content/concepts/_sidebar.md: -------------------------------------------------------------------------------- 1 | * [Quick Start](/content/quick_start) 2 | 3 | * [API Reference](/content/api_reference) 4 | 5 | * [Release Notes](/content/release_notes) 6 | -------------------------------------------------------------------------------- /docs/content/quick_start.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | **This software deploys a web service using AWS infrastructure with endpoints that convert HTML documents into PDF documents.** 4 | 5 | ## Requirements 6 | 7 | * Create an [Amazon Web Services](https://aws.amazon.com/) account — sign up [here](https://portal.aws.amazon.com/billing/signup#/start). 8 | * Install the command line tools for your AWS account — here are the [instructions](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html). 9 | * Install [Node.js](https://nodejs.org/en/) — we recommend using the [Node Version Manager](https://github.com/nvm-sh/nvm). 10 | * Install the [Yarn](https://yarnpkg.com/) package management tool — here are the [instructions](https://classic.yarnpkg.com/en/docs/install). 11 | * Install the [Serverless](https://www.serverless.com/) framework — here are the [instructions](https://www.serverless.com/framework/docs/providers/aws/guide/installation/). 12 | 13 | ## Technical Overview 14 | 15 | * A web application is created using the [AWS API Gateway](https://aws.amazon.com/api-gateway/) service. 16 | * Any HTTP requests received by the web application are handled by an [AWS Lambda Function](https://aws.amazon.com/lambda/). 17 | * The Lambda Function loads [Puppeteer](https://developers.google.com/web/tools/puppeteer) and [Chromium](https://www.chromium.org/) from an [AWS Lambda Layer](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html). 18 | * The Lambda Function uses Puppeteer and Chromium to convert an HTML document into a PDF file. 19 | 20 | ## Deployment 21 | 22 | From the project's root directory, install the required dependencies. 23 | 24 | ```shell 25 | yarn install 26 | ``` 27 | 28 | Then, execute the deployment, as follows: 29 | 30 | ``` 31 | yarn deploy:prod 32 | ``` 33 | 34 | First, the deployment process will execute a `make` command, building the Lambda Layer package (which houses Puppeteer and Chromium). Then, it will configure AWS services (e.g. API Gateway, Lambda, etc). Obviously, your AWS account must have the sufficient access to perform these operations. 35 | 36 | ## Endpoints 37 | 38 | Your web service exposes one endpoint: 39 | 40 | * ```/print``` - Renders the PDF document using Puppeteer (and Chromium). 41 | 42 | The endpoint expects a JSON document, with the following properties: 43 | 44 | * ```html``` - A required ```String``` which contains the HTML document itself. 45 | * ```source``` - An optional ```String``` describing the document. 46 | * ```settings``` - An optional ```Object``` which contains [puppeteer options](https://github.com/puppeteer/puppeteer/blob/v5.2.1/docs/api.md#pagepdfoptions). 47 | 48 | Here is an example: 49 | 50 | ```json 51 | { 52 | "source": "testing", 53 | "html": "

Hello World

You have converted an HTML document into a PDF file.
" 54 | } 55 | ``` 56 | 57 | ## Testing 58 | 59 | Before deployment, you can run the code locally - simulating the AWS infrastructure, as follows: 60 | 61 | ```shell 62 | cd ./packages/api 63 | 64 | sls invoke local -f print --path print/test/print.json 65 | sls invoke local -f print --path print/test/print.5MB.json 66 | ``` 67 | 68 | After deployment, you can test the web service by issuing a ```POST``` request to the actual web service. Here is an example cURL command (obviously, you'll need to correct the hostname and stage name). 69 | 70 | ```shell 71 | curl 'https://{api-identifier}.execute-api.us-east-1.amazonaws.com/{stage|prod}/print' \ 72 | -X 'POST' \ 73 | --output testing.pdf \ 74 | --data-binary '{"source":"testing","html":"

Hello World

You have converted an HTML document into a PDF file.
"}' 75 | ``` 76 | 77 | ## Limitations 78 | 79 | * The size of the HTML document cannot exceed 10 MB (a limit of API Gateway service). 80 | * The processing time required to generate the PDF file cannot exceed 30 seconds (a limit of API Gateway service). 81 | 82 | In theory, these limits can be overcome. However, additional engineering would be required. At present, there is no plan to make these enhancements. 83 | -------------------------------------------------------------------------------- /docs/content/release_notes.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | ## 3.0.1 4 | **Major Changes** 5 | 6 | * Updated [Chromium](https://www.chromium.org/) to version 122 (from version 116). 7 | * Updated [Puppeteer](https://pptr.dev/) to version 22 (from version 21). 8 | 9 | 10 | **Technical Enhancements** 11 | 12 | * Updated AWS CodeBuild integration to use Node.js version 20. 13 | * Updated Node.js runtime to `nodejs20.x` for all Lambda functions (and layers). 14 | * Updated the minimum required version of [`serverless`](https://www.serverless.com/). 15 | * Updated dependencies implicitly. 16 | 17 | ## 3.0.0 18 | **Major Changes** 19 | 20 | * Updated [Chromium](https://www.chromium.org/) to version 116 (from version 92). 21 | * Updated [Puppeteer](https://pptr.dev/) to version 21 (from version 10). 22 | 23 | **Bug Fixes** 24 | 25 | * Corrected inability to run on Node.js versions 16 and 18 by swapping [`aws-chrome-lambda`](https://github.com/alixaxel/chrome-aws-lambda) for [`@sparticuz/chromium`](https://github.com/Sparticuz/chromium). 26 | 27 | **Technical Enhancements** 28 | 29 | * Updated Node.js runtime to `nodejs18.x` for all Lambda functions (and layers). 30 | * Updated the minimum required version of [`serverless`](https://www.serverless.com/). 31 | * Updated [Lerna](https://lerna.js.org/) by three major versions. 32 | * Updated other dependencies explicitly and implicitly. 33 | 34 | ## 2.1.1 35 | **Other** 36 | 37 | * Minor corrections to [documentation website](https://barchart.github.io/aws-lambda-pdf-generator/#). 38 | * Regenerated `yarn.lock` file. 39 | 40 | ## 2.1.0 41 | **New Features** 42 | 43 | * Added the `service/version` endpoint to the API. 44 | 45 | **Technical Enhancements** 46 | 47 | * Updated Node.js runtime to version 14. 48 | * Updated AWS CodeBuild to use Node.js version 14. 49 | * Updated the Serverless framework to version 3. 50 | * Updated the [`puppeteer`](https://github.com/puppeteer/puppeteer), [`puppeteer-core`](https://github.com/puppeteer/puppeteer) and [`chrome-aws-lambda`](https://github.com/alixaxel/chrome-aws-lambda) libraries to version 10.1.0. Puppeteer version 14 is available; however, at present the chrome-aws-lambda library does not support any version past 10.1.0. More detail can be found in the [`README.md`](https://github.com/barchart/aws-lambda-pdf-generator/tree/master/packages/api#technical-notes) file for the API. 51 | * Updated various other dependencies. 52 | 53 | ## 2.0.0 54 | **Breaking Changes** 55 | 56 | * Removed `print/v81` and `print/v83` endpoints from the API. Only the `print` endpoint remains. 57 | * Renamed application. Avoid problems by removing previous installations first (see the `sls remove` command). 58 | 59 | **New Features** 60 | 61 | * Generation of PDF files larger than 10 MB is now supported. (using gzip compression or an HTTP 303 response). 62 | 63 | **Technical Enhancements** 64 | 65 | * Converted project to use [Lerna](https://github.com/lerna/lerna) approach. 66 | * Upgraded [Serverless](https://www.serverless.com/) framework to version 2.13.0 (or better). 67 | * Added descriptions for application and functions to `serverless.yml` file. 68 | * Updated Chromium version from `v83` to `v88`. 69 | 70 | 71 | ## 1.1.1 72 | **No Functional Changes** 73 | 74 | * Finalized [Documentation](https://barchart.github.io/aws-lambda-pdf-generator/#/). 75 | 76 | ## 1.1.0 77 | **New Features** 78 | 79 | * Added ```source``` field as part of the request body. 80 | 81 | **Other** 82 | 83 | * Added printer failure types. 84 | * Improved logging. 85 | 86 | ## 1.0.5 87 | **Initial Public Release** 88 | -------------------------------------------------------------------------------- /docs/content/releases/1.0.5.md: -------------------------------------------------------------------------------- 1 | **Initial Public Release** -------------------------------------------------------------------------------- /docs/content/releases/1.1.0.md: -------------------------------------------------------------------------------- 1 | **New Features** 2 | 3 | * Added ```source``` field as part of the request body. 4 | 5 | **Other** 6 | 7 | * Added printer failure types. 8 | * Improved logging. -------------------------------------------------------------------------------- /docs/content/releases/1.1.1.md: -------------------------------------------------------------------------------- 1 | **No Functional Changes** 2 | 3 | * Finalized [Documentation](https://barchart.github.io/aws-lambda-pdf-generator/#/). -------------------------------------------------------------------------------- /docs/content/releases/2.0.0.md: -------------------------------------------------------------------------------- 1 | **Breaking Changes** 2 | 3 | * Removed `print/v81` and `print/v83` endpoints from the API. Only the `print` endpoint remains. 4 | * Renamed application. Avoid problems by removing previous installations first (see the `sls remove` command). 5 | 6 | **New Features** 7 | 8 | * Generation of PDF files larger than 10 MB is now supported. (using gzip compression or an HTTP 303 response). 9 | 10 | **Technical Enhancements** 11 | 12 | * Converted project to use [Lerna](https://github.com/lerna/lerna) approach. 13 | * Upgraded [Serverless](https://www.serverless.com/) framework to version 2.13.0 (or better). 14 | * Added descriptions for application and functions to `serverless.yml` file. 15 | * Updated Chromium version from `v83` to `v88`. 16 | -------------------------------------------------------------------------------- /docs/content/releases/2.1.0.md: -------------------------------------------------------------------------------- 1 | **New Features** 2 | 3 | * Added the `service/version` endpoint to the API. 4 | 5 | **Technical Enhancements** 6 | 7 | * Updated Node.js runtime to version 14. 8 | * Updated AWS CodeBuild to use Node.js version 14. 9 | * Updated the Serverless framework to version 3. 10 | * Updated the [`puppeteer`](https://github.com/puppeteer/puppeteer), [`puppeteer-core`](https://github.com/puppeteer/puppeteer) and [`chrome-aws-lambda`](https://github.com/alixaxel/chrome-aws-lambda) libraries to version 10.1.0. Puppeteer version 14 is available; however, at present the chrome-aws-lambda library does not support any version past 10.1.0. More detail can be found in the [`README.md`](https://github.com/barchart/aws-lambda-pdf-generator/tree/master/packages/api#technical-notes) file for the API. 11 | * Updated various other dependencies. -------------------------------------------------------------------------------- /docs/content/releases/2.1.1.md: -------------------------------------------------------------------------------- 1 | **Other** 2 | 3 | * Minor corrections to [documentation website](https://barchart.github.io/aws-lambda-pdf-generator/#). 4 | * Regenerated `yarn.lock` file. -------------------------------------------------------------------------------- /docs/content/releases/3.0.0.md: -------------------------------------------------------------------------------- 1 | **Major Changes** 2 | 3 | * Updated [Chromium](https://www.chromium.org/) to version 116 (from version 92). 4 | * Updated [Puppeteer](https://pptr.dev/) to version 21 (from version 10). 5 | 6 | **Bug Fixes** 7 | 8 | * Corrected inability to run on Node.js versions 16 and 18 by swapping [`aws-chrome-lambda`](https://github.com/alixaxel/chrome-aws-lambda) for [`@sparticuz/chromium`](https://github.com/Sparticuz/chromium). 9 | 10 | **Technical Enhancements** 11 | 12 | * Updated Node.js runtime to `nodejs18.x` for all Lambda functions (and layers). 13 | * Updated the minimum required version of [`serverless`](https://www.serverless.com/). 14 | * Updated [Lerna](https://lerna.js.org/) by three major versions. 15 | * Updated other dependencies explicitly and implicitly. -------------------------------------------------------------------------------- /docs/content/releases/3.0.1.md: -------------------------------------------------------------------------------- 1 | **Major Changes** 2 | 3 | * Updated [Chromium](https://www.chromium.org/) to version 122 (from version 116). 4 | * Updated [Puppeteer](https://pptr.dev/) to version 22 (from version 21). 5 | 6 | **Technical Enhancements** 7 | 8 | * Updated AWS CodeBuild integration to use Node.js version 20. 9 | * Updated Node.js runtime to `nodejs20.x` for all Lambda functions (and layers). 10 | * Updated the minimum required version of [`serverless`](https://www.serverless.com/). 11 | * Updated dependencies implicitly. -------------------------------------------------------------------------------- /docs/content/releases/_sidebar.md: -------------------------------------------------------------------------------- 1 | * [Quick Start](/content/quick_start) 2 | 3 | * [API Reference](/content/api_reference) 4 | 5 | * [Release Notes](/content/release_notes) 6 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /docs/static/openapi.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.1 2 | 3 | info: 4 | title: PDF generator API 5 | description: PDF generator API 6 | version: 2.1.0 7 | 8 | components: 9 | responses: 10 | Success: 11 | description: Generated PDF file. 12 | content: 13 | application/pdf: 14 | schema: 15 | type: string 16 | format: binary 17 | 18 | BadRequest: 19 | description: Bad request. 20 | content: 21 | application/json: 22 | schema: 23 | type: array 24 | items: 25 | type: object 26 | properties: 27 | value: 28 | type: object 29 | properties: 30 | code: 31 | type: string 32 | example: PRINT_FAILED_NO_HTML_LAYOUT 33 | message: 34 | type: string 35 | example: Failed to print PDF, missing html layout. 36 | children: 37 | type: array 38 | example: [] 39 | items: {} 40 | 41 | ServerError: 42 | description: Server error. 43 | content: 44 | application/json: 45 | schema: 46 | type: array 47 | items: 48 | type: object 49 | properties: 50 | value: 51 | type: object 52 | properties: 53 | code: 54 | type: string 55 | example: REQUEST_GENERAL_FAILURE 56 | message: 57 | type: string 58 | example: An attempt to print html page failed for unspecified reason(s). 59 | children: 60 | type: array 61 | example: [] 62 | items: {} 63 | 64 | tags: 65 | - name: Pdf 66 | 67 | paths: 68 | /print: 69 | post: 70 | summary: Prints PDF page from html layout (chrome v92 by default). 71 | description: Prints PDF page from html layout (chrome v92 by default). 72 | tags: 73 | - Pdf 74 | requestBody: 75 | content: 76 | application/json: 77 | schema: 78 | type: object 79 | properties: 80 | source: 81 | type: string 82 | description: Optional string to define the source of request 83 | example: crude-oil-price-report 84 | html: 85 | type: string 86 | description: HTML layout to print 87 | example: 'test' 88 | settings: 89 | type: object 90 | description: 'Optional [settings](https://github.com/puppeteer/puppeteer/blob/v5.2.1/docs/api.md#pagepdfoptions) passed to page.pdf()' 91 | example: { width: '100px' } 92 | required: 93 | - html 94 | responses: 95 | '200': 96 | $ref: '#/components/responses/Success' 97 | '400': 98 | $ref: '#/components/responses/BadRequest' 99 | '500': 100 | $ref: '#/components/responses/ServerError' 101 | -------------------------------------------------------------------------------- /docs/styles/override.css: -------------------------------------------------------------------------------- 1 | .sidebar .app-name { 2 | text-align: left; 3 | margin-left: 15px; 4 | } 5 | 6 | .sidebar ul li { 7 | font-size: 14px; 8 | color: #505d6b; 9 | } 10 | 11 | h2#contents + ul p { 12 | margin: 0; 13 | } 14 | 15 | main section.content { 16 | padding-top: 0; 17 | } 18 | 19 | iframe#swagger { 20 | display: block; 21 | width: 100%; 22 | height: 100vh; 23 | border: none; 24 | } -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "npmClient": "yarn", 3 | "packages": [ 4 | "packages/*" 5 | ], 6 | "version": "3.0.1", 7 | "useWorkspaces": true 8 | } 9 | -------------------------------------------------------------------------------- /openapi.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.1 2 | 3 | info: 4 | title: PDF generator API 5 | description: PDF generator API 6 | version: 2.1.0 7 | 8 | components: 9 | responses: 10 | Success: 11 | description: Generated PDF file. 12 | content: 13 | application/pdf: 14 | schema: 15 | type: string 16 | format: binary 17 | 18 | BadRequest: 19 | description: Bad request. 20 | content: 21 | application/json: 22 | schema: 23 | type: array 24 | items: 25 | type: object 26 | properties: 27 | value: 28 | type: object 29 | properties: 30 | code: 31 | type: string 32 | example: PRINT_FAILED_NO_HTML_LAYOUT 33 | message: 34 | type: string 35 | example: Failed to print PDF, missing html layout. 36 | children: 37 | type: array 38 | example: [] 39 | items: {} 40 | 41 | ServerError: 42 | description: Server error. 43 | content: 44 | application/json: 45 | schema: 46 | type: array 47 | items: 48 | type: object 49 | properties: 50 | value: 51 | type: object 52 | properties: 53 | code: 54 | type: string 55 | example: REQUEST_GENERAL_FAILURE 56 | message: 57 | type: string 58 | example: An attempt to print html page failed for unspecified reason(s). 59 | children: 60 | type: array 61 | example: [] 62 | items: {} 63 | 64 | tags: 65 | - name: Pdf 66 | 67 | paths: 68 | /print: 69 | post: 70 | summary: Prints PDF page from html layout (chrome v92 by default). 71 | description: Prints PDF page from html layout (chrome v92 by default). 72 | tags: 73 | - Pdf 74 | requestBody: 75 | content: 76 | application/json: 77 | schema: 78 | type: object 79 | properties: 80 | source: 81 | type: string 82 | description: Optional string to define the source of request 83 | example: crude-oil-price-report 84 | html: 85 | type: string 86 | description: HTML layout to print 87 | example: 'test' 88 | settings: 89 | type: object 90 | description: 'Optional [settings](https://github.com/puppeteer/puppeteer/blob/v5.2.1/docs/api.md#pagepdfoptions) passed to page.pdf()' 91 | example: { width: '100px' } 92 | required: 93 | - html 94 | responses: 95 | '200': 96 | $ref: '#/components/responses/Success' 97 | '400': 98 | $ref: '#/components/responses/BadRequest' 99 | '500': 100 | $ref: '#/components/responses/ServerError' 101 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@barchart/aws-lambda-pdf-generator", 3 | "workspaces": [ 4 | "packages/*" 5 | ], 6 | "repository": { 7 | "type": "git", 8 | "url": "git+ssh://git@github.com/barchart/barchart/aws-lambda-pdf-generator.git" 9 | }, 10 | "scripts": { 11 | "clean": "lerna clean --yes && rm -rf node_modules", 12 | "lint": "lerna run lint --stream", 13 | "preversion": "git diff --exit-code", 14 | "release": "lerna version -m 'Release. Bump version number %v' --tag-version-prefix='' --force-publish", 15 | "deploy:stage": "lerna run deploy:stage --stream", 16 | "deploy:prod": "lerna run deploy:prod --stream" 17 | }, 18 | "devDependencies": { 19 | "jshint": "^2.13.6", 20 | "lerna": "^6.6.2", 21 | "serverless-apigw-binary": "^0.4.4", 22 | "serverless-webpack": "^5.5.0", 23 | "webpack": "^5.11.1" 24 | }, 25 | "license": "MIT", 26 | "private": true 27 | } 28 | -------------------------------------------------------------------------------- /packages/api/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean 2 | 3 | .DEFAULT_GOAL := all 4 | 5 | CHROME_REPO=https://github.com/Sparticuz/chromium.git 6 | LAYERS_FOLDER=layers 7 | 8 | all: ${LAYERS_FOLDER}/chromium-v122.zip 9 | 10 | ${LAYERS_FOLDER}/chromium-v122.zip: 11 | @echo 'Downloading...' \ 12 | && git clone $(CHROME_REPO) chromium-aws-lambda --depth 1 --branch v122.0.0 \ 13 | && echo 'Making .zip archive...' \ 14 | && cd chromium-aws-lambda \ 15 | && rm -rf node_modules \ 16 | && npm i \ 17 | && make chromium-aws-lambda.zip && mkdir -p ./../layers && mv chromium-aws-lambda.zip ./../${LAYERS_FOLDER}/chromium-v122.zip \ 18 | && echo 'Removing unnecessary stuff...' \ 19 | && cd ../ && rm -rf chromium-aws-lambda \ 20 | && echo 'Done' 21 | 22 | clean: 23 | rm -rf layers && rm -rf chromium-aws-lambda 24 | -------------------------------------------------------------------------------- /packages/api/README.md: -------------------------------------------------------------------------------- 1 | # @barchart/serverless-pdf-generator 2 | 3 | ### Serverless 4 | 5 | This project uses the [Serverless](https://serverless.com/) framework for testing and deployment. 6 | 7 | #### Installation 8 | 9 | ```shell 10 | yarn global add serverless 11 | ``` 12 | 13 | #### Deployment 14 | 15 | Deploy the service from the project's root directory. 16 | 17 | ```shell 18 | cd ./../.. 19 | yarn install 20 | yarn run deploy:[stage|prod] 21 | ``` 22 | 23 | ### Testing 24 | 25 | #### Local Testing 26 | 27 | ```shell 28 | sls invoke local -f print --path print/test/print.json 29 | sls invoke local -f print --path print/test/print.5MB.json 30 | 31 | sls invoke local -f serviceRead --path service/test/version.json 32 | ``` 33 | 34 | #### Remote Testing 35 | 36 | ```shell 37 | curl 'https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/stage/print' \ 38 | -X 'POST' \ 39 | --output testing.pdf \ 40 | --data-binary '{"source":"testing","html":"

Hello World

You have converted an HTML document into a PDF file.
"}' 41 | ``` 42 | 43 | ### Technical Notes 44 | 45 | **ARM Architecture (2023/08/31, v3.0.0)** 46 | 47 | > It would be nice to use ARM architecture for AWS Lambda functions (and use Apple silicon locally). However, the [`@sparticuz/chromium`](https://github.com/Sparticuz/chromium) project has not yet added support. See the comments [here](https://github.com/Sparticuz/chromium#running-locally--headlessheadful-mode). -------------------------------------------------------------------------------- /packages/api/common/api/PrinterFailureTypes.js: -------------------------------------------------------------------------------- 1 | const FailureType = require('@barchart/common-js/api/failures/FailureType'); 2 | 3 | module.exports = (() => { 4 | 'use strict'; 5 | 6 | /** 7 | * A static container for print specific {@link FailureType} items. 8 | * 9 | * @public 10 | */ 11 | class PrinterFailureTypes { 12 | constructor() { 13 | 14 | } 15 | 16 | static get PRINT_FAILED_HTML_MISSING() { 17 | return printFailedHtmlMissing; 18 | } 19 | 20 | toString() { 21 | return '[PrinterFailureTypes]'; 22 | } 23 | } 24 | 25 | const printFailedHtmlMissing = new FailureType('PRINT_FAILED_HTML_MISSING', 'Failed to print PDF, no HTML document provided.', false, 400); 26 | 27 | return PrinterFailureTypes; 28 | })(); 29 | -------------------------------------------------------------------------------- /packages/api/common/aws/LambdaHelper.js: -------------------------------------------------------------------------------- 1 | require('source-map-support').install(); 2 | 3 | const LambdaHelper = require('@barchart/common-node-js/aws/lambda/LambdaHelper'); 4 | 5 | const logConfiguration = require('./../../log4js.config'); 6 | 7 | module.exports = (() => { 8 | 'use strict'; 9 | 10 | const getLogger = LambdaHelper.getLogger; 11 | const execute = LambdaHelper.process; 12 | 13 | Object.assign(LambdaHelper, { 14 | process(description, event, callback, processor) { 15 | return Promise.resolve() 16 | .then(() => { 17 | const logger = this.getLogger(); 18 | 19 | logger.info('Starting processing [', description, ']'); 20 | 21 | return execute(description, event, callback, processor) 22 | .then(() => { 23 | logger.info('Finished [', description, ']'); 24 | }); 25 | }); 26 | }, 27 | 28 | getLogger() { 29 | return getLogger(logConfiguration.default); 30 | } 31 | }); 32 | 33 | return LambdaHelper; 34 | })(); 35 | 36 | -------------------------------------------------------------------------------- /packages/api/gulpfile.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | const gulp = require('gulp'), 4 | replace = require('gulp-replace'); 5 | 6 | function getVersionFromPackage() { 7 | return JSON.parse(fs.readFileSync('./package.json', 'utf8')).version; 8 | } 9 | 10 | const rootPath = './../../'; 11 | 12 | gulp.task('embed-version', () => { 13 | const version = getVersionFromPackage(); 14 | 15 | const coverpage = gulp.src([rootPath + 'docs/_coverpage.md']) 16 | .pipe(replace(/(>)([0-9]+\.[0-9]+\.[0-9]+.*)(<)/g, '$1' + version + '$3')) 17 | .pipe(gulp.dest(rootPath + 'docs/')); 18 | 19 | return coverpage; 20 | }); 21 | -------------------------------------------------------------------------------- /packages/api/log4js.config.js: -------------------------------------------------------------------------------- 1 | const appenders = require('@barchart/log4js-node-appenders'); 2 | 3 | const getPattern = colored => colored ? '%[%c - %m% %]' : '%c - %m%'; 4 | 5 | module.exports = { 6 | default: { 7 | categories: { 8 | default: { appenders: [ 'lambda' ], level: 'debug' }, 9 | 'LambdaHelper/Event': { appenders: [ 'lambda' ], level: 'debug' } 10 | }, 11 | appenders: { 12 | lambda: { 13 | type: appenders.lambda, 14 | layout: { 15 | type: 'pattern', 16 | pattern: getPattern(process.env.IS_LOCAL) 17 | } 18 | } 19 | } 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /packages/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@barchart/serverless-pdf-generator", 3 | "version": "3.0.1", 4 | "description": "Web service for conversion of HTML to PDF", 5 | "contributors": [ 6 | { 7 | "name": "Bryan Ingle", 8 | "email": "bryan.ingle@barchart.com" 9 | }, 10 | { 11 | "name": "Igor Losev", 12 | "email": "igor.losev@barchart.com" 13 | } 14 | ], 15 | "scripts": { 16 | "lint": "jshint ./ --exclude './node_modules/**'", 17 | "predeploy:stage": "make clean && make all", 18 | "predeploy:prod": "make clean && make all", 19 | "deploy:stage": "serverless deploy --stage stage", 20 | "deploy:prod": "serverless deploy --stage prod", 21 | "version": "gulp embed-version" 22 | }, 23 | "dependencies": { 24 | "@barchart/common-js": "^4.21.0", 25 | "@barchart/common-node-js": "^4.21.0", 26 | "@barchart/log4js-node-appenders": "^1.1.5", 27 | "@sparticuz/chromium": "122.0.0", 28 | "puppeteer-core": "22.4.0", 29 | "source-map-support": "^0.5.21" 30 | }, 31 | "devDependencies": { 32 | "gulp": "^4.0.2", 33 | "gulp-replace": "^0.5.4", 34 | "puppeteer": "21.1.1" 35 | }, 36 | "license": "MIT", 37 | "private": true 38 | } 39 | -------------------------------------------------------------------------------- /packages/api/print/print.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | const chromium = require('@sparticuz/chromium'), 4 | puppeteer = require('puppeteer-core'); 5 | 6 | const FailureReason = require('@barchart/common-js/api/failures/FailureReason'), 7 | PrinterFailureTypes = require('./../common/api/PrinterFailureTypes'); 8 | 9 | const LambdaResponseGeneratorGzip = require('@barchart/common-node-js/aws/lambda/responses/LambdaResponseGeneratorGzip'), 10 | LambdaResponseGeneratorS3 = require('@barchart/common-node-js/aws/lambda/responses/LambdaResponseGeneratorS3'); 11 | 12 | const LambdaHelper = require('./../common/aws/LambdaHelper'); 13 | 14 | module.exports = (() => { 15 | 'use strict'; 16 | 17 | return { 18 | handler: (event, lambdaContext, callback) => { 19 | LambdaHelper.process('Print HTML page', event, callback, async (parser, responder) => { 20 | const logger = LambdaHelper.getLogger(); 21 | 22 | responder.addResponseGenerators([ 23 | new LambdaResponseGeneratorGzip(parser), 24 | new LambdaResponseGeneratorS3() 25 | ]); 26 | 27 | const body = JSON.parse(Buffer.from(parser.getBody(), 'base64').toString()); 28 | 29 | const source = body.source || null; 30 | const html = body.html || null; 31 | const settings = body.settings || null; 32 | 33 | if (html === null) { 34 | return Promise.reject(FailureReason.from(PrinterFailureTypes.PRINT_FAILED_HTML_MISSING)); 35 | } 36 | 37 | const context = { }; 38 | 39 | context.browser = null; 40 | context.version = null; 41 | 42 | try { 43 | logger.debug(`Launching headless chrome for [ ${source} ]`); 44 | 45 | chromium.setGraphicsMode = false; 46 | 47 | context.browser = await puppeteer.launch({ 48 | args: chromium.args, 49 | executablePath: await chromium.executablePath(), 50 | headless: chromium.headless, 51 | ignoreHTTPSErrors: true 52 | }); 53 | 54 | context.version = await context.browser.version(); 55 | 56 | logger.info(`Launched headless chrome [ ${context.version} ] for [ ${source} ]`); 57 | 58 | const page = await context.browser.newPage(); 59 | 60 | page.on('console', (message) => { 61 | for (let i = 0; i < message.args().length; ++i) { 62 | logger.info(`[ ${i} ] Console: ${message.args()[i]}`); 63 | } 64 | }); 65 | 66 | page.on('pageerror', (error) => { 67 | logger.error(error); 68 | }); 69 | 70 | logger.debug(`Printing HTML layout for [ ${source} ] [ ${html.length} ] as PDF`); 71 | 72 | await page.setContent(html, { waitUntil: 'networkidle0' }); 73 | 74 | context.pdf = await page.pdf(settings || { }); 75 | 76 | logger.info(`Printed HTML layout for [ ${source} ] [ ${html.length} ] as PDF`); 77 | 78 | await page.close(); 79 | } catch (e) { 80 | logger.error(e); 81 | 82 | throw e; 83 | } finally { 84 | if (context.browser !== null) { 85 | logger.debug(`Closing headless chrome [ ${context.version} ] for [ ${source} ]`); 86 | 87 | await context.browser.close(); 88 | 89 | logger.info(`Closed headless chrome [ ${context.version} ] for [ ${source} ]`); 90 | } 91 | } 92 | 93 | if (false) { 94 | fs.writeFile('./out.pdf', context.pdf); 95 | } 96 | 97 | return responder 98 | .setHeader('Content-Type', 'application/pdf') 99 | .send(context.pdf); 100 | }); 101 | } 102 | }; 103 | })(); 104 | -------------------------------------------------------------------------------- /packages/api/print/test/print.5MB.json: -------------------------------------------------------------------------------- 1 | { 2 | "resource": "/print", 3 | "path": "/print", 4 | "httpMethod": "POST", 5 | "headers": { 6 | "Accept": "*/*", 7 | "Accept-Encoding": "gzip, deflate", 8 | "cache-control": "no-cache", 9 | "CloudFront-Forwarded-Proto": "https", 10 | "CloudFront-Is-Desktop-Viewer": "true", 11 | "CloudFront-Is-Mobile-Viewer": "false", 12 | "CloudFront-Is-SmartTV-Viewer": "false", 13 | "CloudFront-Is-Tablet-Viewer": "false", 14 | "CloudFront-Viewer-Country": "PL", 15 | "Content-Type": "text/plain", 16 | "Host": "zh485z5j6g.execute-api.us-east-1.amazonaws.com", 17 | "Postman-Token": "6d7f1ff1-49f8-4f51-8029-6242ad894521", 18 | "User-Agent": "PostmanRuntime/7.1.5", 19 | "Via": "1.1 7cbbe7c1ce97c17d13c405bf29da748d.cloudfront.net (CloudFront)", 20 | "X-Amz-Cf-Id": "ERnt-g2O5gf7VA-zJxbWI7dOceRQwkGP6JHnsI2-1FPqIgSlSnGzDQ==", 21 | "X-Amzn-Trace-Id": "Root=1-5fcd7a22-7eaffd834484e1a52707d91a", 22 | "X-Forwarded-For": "80.238.108.122, 54.239.171.99", 23 | "X-Forwarded-Port": "443", 24 | "X-Forwarded-Proto": "https" 25 | }, 26 | "multiValueHeaders": { 27 | "Accept": [ 28 | "*/*" 29 | ], 30 | "Accept-Encoding": [ 31 | "gzip, deflate" 32 | ], 33 | "cache-control": [ 34 | "no-cache" 35 | ], 36 | "CloudFront-Forwarded-Proto": [ 37 | "https" 38 | ], 39 | "CloudFront-Is-Desktop-Viewer": [ 40 | "true" 41 | ], 42 | "CloudFront-Is-Mobile-Viewer": [ 43 | "false" 44 | ], 45 | "CloudFront-Is-SmartTV-Viewer": [ 46 | "false" 47 | ], 48 | "CloudFront-Is-Tablet-Viewer": [ 49 | "false" 50 | ], 51 | "CloudFront-Viewer-Country": [ 52 | "PL" 53 | ], 54 | "Content-Type": [ 55 | "text/plain" 56 | ], 57 | "Host": [ 58 | "zh485z5j6g.execute-api.us-east-1.amazonaws.com" 59 | ], 60 | "Postman-Token": [ 61 | "6d7f1ff1-49f8-4f51-8029-6242ad894521" 62 | ], 63 | "User-Agent": [ 64 | "PostmanRuntime/7.1.5" 65 | ], 66 | "Via": [ 67 | "1.1 7cbbe7c1ce97c17d13c405bf29da748d.cloudfront.net (CloudFront)" 68 | ], 69 | "X-Amz-Cf-Id": [ 70 | "ERnt-g2O5gf7VA-zJxbWI7dOceRQwkGP6JHnsI2-1FPqIgSlSnGzDQ==" 71 | ], 72 | "X-Amzn-Trace-Id": [ 73 | "Root=1-5fcd7a22-7eaffd834484e1a52707d91a" 74 | ], 75 | "X-Forwarded-For": [ 76 | "80.238.108.122, 54.239.171.99" 77 | ], 78 | "X-Forwarded-Port": [ 79 | "443" 80 | ], 81 | "X-Forwarded-Proto": [ 82 | "https" 83 | ] 84 | }, 85 | "queryStringParameters": null, 86 | "multiValueQueryStringParameters": null, 87 | "pathParameters": null, 88 | "stageVariables": null, 89 | "requestContext": { 90 | "resourceId": "tp72cl", 91 | "resourcePath": "/print", 92 | "httpMethod": "POST", 93 | "extendedRequestId": "XKAFdFlnIAMFsHQ=", 94 | "requestTime": "07/Dec/2020:00:41:06 +0000", 95 | "path": "/stage/print", 96 | "accountId": "081121675754", 97 | "protocol": "HTTP/1.1", 98 | "stage": "stage", 99 | "domainPrefix": "zh485z5j6g", 100 | "requestTimeEpoch": 1607301666882, 101 | "requestId": "56de0b8e-8107-4f2d-bce7-b5d25f72fdd9", 102 | "identity": { 103 | "cognitoIdentityPoolId": null, 104 | "accountId": null, 105 | "cognitoIdentityId": null, 106 | "caller": null, 107 | "sourceIp": "80.238.108.122", 108 | "principalOrgId": null, 109 | "accessKey": null, 110 | "cognitoAuthenticationType": null, 111 | "cognitoAuthenticationProvider": null, 112 | "userArn": null, 113 | "userAgent": "PostmanRuntime/7.1.5", 114 | "user": null 115 | }, 116 | "domainName": "zh485z5j6g.execute-api.us-east-1.amazonaws.com", 117 | "apiId": "zh485z5j6g" 118 | }, 119 | "body": "eyJzb3VyY2UiOiJ0ZXN0aW5nIiwiaHRtbCI6IjxodG1sPjxib2R5PjxoMT5IZWxsbyBXb3JsZDwvaDE+PGltZyBzcmM9XCJodHRwczovL3VwbG9hZC53aWtpbWVkaWEub3JnL3dpa2lwZWRpYS9jb21tb25zLzIvMmQvU25ha2VfUml2ZXJfJTI4NW1iJTI5LmpwZ1wiPjxkaXY+WW91IGhhdmUgY29udmVydGVkIGFuIEhUTUwgZG9jdW1lbnQgaW50byBhIFBERiBmaWxlLjwvZGl2PjxoMT5IZWxsbyBXb3JsZDwvaDE+PGRpdj5Zb3UgaGF2ZSBjb252ZXJ0ZWQgYW4gSFRNTCBkb2N1bWVudCBpbnRvIGEgUERGIGZpbGUuPC9kaXY+PGgxPkhlbGxvIFdvcmxkPC9oMT4ifQ==", 120 | "isBase64Encoded": true 121 | } 122 | -------------------------------------------------------------------------------- /packages/api/print/test/print.json: -------------------------------------------------------------------------------- 1 | { 2 | "resource": "/print/chrome/v83", 3 | "path": "/print/chrome/v83", 4 | "httpMethod": "POST", 5 | "headers": { 6 | "Accept": "*/*", 7 | "CloudFront-Forwarded-Proto": "https", 8 | "CloudFront-Is-Desktop-Viewer": "true", 9 | "CloudFront-Is-Mobile-Viewer": "false", 10 | "CloudFront-Is-SmartTV-Viewer": "false", 11 | "CloudFront-Is-Tablet-Viewer": "false", 12 | "CloudFront-Viewer-Country": "US", 13 | "content-type": "application/x-www-form-urlencoded", 14 | "Host": "uvw4xxcjtg.execute-api.us-east-1.amazonaws.com", 15 | "User-Agent": "curl/7.64.1", 16 | "Via": "2.0 14d8203abb1cc1bf1e44a309f8dc1a75.cloudfront.net (CloudFront)", 17 | "X-Amz-Cf-Id": "tSk9dYnlTJvnBdNStgZFbXPXfAFbnBjDy1hnqj8k7Bah-_SuLGfN6w==", 18 | "X-Amzn-Trace-Id": "Root=1-5f3c1150-2b8fdc69c84c10b0a4099710", 19 | "X-Forwarded-For": "108.160.194.150, 70.132.57.152", 20 | "X-Forwarded-Port": "443", 21 | "X-Forwarded-Proto": "https" 22 | }, 23 | "multiValueHeaders": { 24 | "Accept": [ 25 | "*/*" 26 | ], 27 | "CloudFront-Forwarded-Proto": [ 28 | "https" 29 | ], 30 | "CloudFront-Is-Desktop-Viewer": [ 31 | "true" 32 | ], 33 | "CloudFront-Is-Mobile-Viewer": [ 34 | "false" 35 | ], 36 | "CloudFront-Is-SmartTV-Viewer": [ 37 | "false" 38 | ], 39 | "CloudFront-Is-Tablet-Viewer": [ 40 | "false" 41 | ], 42 | "CloudFront-Viewer-Country": [ 43 | "US" 44 | ], 45 | "content-type": [ 46 | "application/x-www-form-urlencoded" 47 | ], 48 | "Host": [ 49 | "uvw4xxcjtg.execute-api.us-east-1.amazonaws.com" 50 | ], 51 | "User-Agent": [ 52 | "curl/7.64.1" 53 | ], 54 | "Via": [ 55 | "2.0 14d8203abb1cc1bf1e44a309f8dc1a75.cloudfront.net (CloudFront)" 56 | ], 57 | "X-Amz-Cf-Id": [ 58 | "tSk9dYnlTJvnBdNStgZFbXPXfAFbnBjDy1hnqj8k7Bah-_SuLGfN6w==" 59 | ], 60 | "X-Amzn-Trace-Id": [ 61 | "Root=1-5f3c1150-2b8fdc69c84c10b0a4099710" 62 | ], 63 | "X-Forwarded-For": [ 64 | "108.160.194.150, 70.132.57.152" 65 | ], 66 | "X-Forwarded-Port": [ 67 | "443" 68 | ], 69 | "X-Forwarded-Proto": [ 70 | "https" 71 | ] 72 | }, 73 | "queryStringParameters": null, 74 | "multiValueQueryStringParameters": null, 75 | "pathParameters": null, 76 | "stageVariables": null, 77 | "requestContext": { 78 | "resourceId": "zr6kav", 79 | "resourcePath": "/print/chrome/v83", 80 | "httpMethod": "POST", 81 | "extendedRequestId": "ReeknEiaoAMF2NQ=", 82 | "requestTime": "18/Aug/2020:17:35:12 +0000", 83 | "path": "/test/print/chrome/v83", 84 | "accountId": "081121675754", 85 | "protocol": "HTTP/1.1", 86 | "stage": "test", 87 | "domainPrefix": "uvw4xxcjtg", 88 | "requestTimeEpoch": 1597772112646, 89 | "requestId": "61f74982-6e44-4cc5-9d76-0346bd8974bc", 90 | "identity": { 91 | "cognitoIdentityPoolId": null, 92 | "accountId": null, 93 | "cognitoIdentityId": null, 94 | "caller": null, 95 | "sourceIp": "108.160.194.150", 96 | "principalOrgId": null, 97 | "accessKey": null, 98 | "cognitoAuthenticationType": null, 99 | "cognitoAuthenticationProvider": null, 100 | "userArn": null, 101 | "userAgent": "curl/7.64.1", 102 | "user": null 103 | }, 104 | "domainName": "uvw4xxcjtg.execute-api.us-east-1.amazonaws.com", 105 | "apiId": "uvw4xxcjtg" 106 | }, 107 | "body": "eyJzb3VyY2UiOiJ0ZXN0aW5nIiwiaHRtbCI6IjxodG1sPjxib2R5PjxoMT5IZWxsbyBXb3JsZDwvaDE+PGRpdj5Zb3UgaGF2ZSBjb252ZXJ0ZWQgYW4gSFRNTCBkb2N1bWVudCBpbnRvIGEgUERGIGZpbGUuPC9kaXY+PC9ib2R5PjwvaHRtbD4ifQ==", 108 | "isBase64Encoded": true 109 | } -------------------------------------------------------------------------------- /packages/api/serverless.yml: -------------------------------------------------------------------------------- 1 | service: serverless-pdf-generator 2 | 3 | frameworkVersion: ">=3.38.0 <4.0.0" 4 | 5 | custom: 6 | stage: ${opt:stage, 'stage'} 7 | 8 | s3: 9 | responseRegion: 'us-east-1' 10 | responseBucket: 'barchart-aws-lambda-responses' 11 | 12 | cors: 13 | origin: '*' 14 | headers: 15 | - Accept-Encoding 16 | - Content-Type 17 | - Content-Length 18 | - X-Amz-Date 19 | - Authorization 20 | - X-Api-Key 21 | - X-Amz-Security-Token 22 | - X-Amz-User-Agent 23 | - Access-Control-Allow-Origin 24 | - Access-Control-Allow-Headers 25 | - Access-Control-Allow-Methods 26 | allowCredentials: false 27 | 28 | apigwBinary: 29 | types: 30 | - '*/*' 31 | 32 | provider: 33 | name: aws 34 | runtime: nodejs20.x 35 | logRetentionInDays: 90 36 | deploymentBucket: barchart-serverless-deployments 37 | 38 | environment: 39 | NODE_ENV: ${self:custom.stage} 40 | S3_LARGE_HTTP_RESPONSE_REGION: ${self:custom.s3.responseRegion} 41 | S3_LARGE_HTTP_RESPONSE_BUCKET: ${self:custom.s3.responseBucket} 42 | 43 | iam: 44 | role: 45 | statements: 46 | - Effect: Allow 47 | Action: 48 | - s3:GetObject 49 | - s3:PutObject 50 | Resource: arn:aws:s3:::${self:custom.s3.responseBucket}/* 51 | 52 | apiGateway: 53 | shouldStartNameWithService: true 54 | 55 | package: 56 | individually: true 57 | 58 | layers: 59 | HeadlessChromium: 60 | name: serverless-headless-chromium-${self:custom.stage}-v122 61 | description: Layer for puppeteer to launch headless Chromium v122 62 | compatibleRuntimes: 63 | - nodejs20.x 64 | package: 65 | artifact: layers/chromium-v122.zip 66 | 67 | functions: 68 | print: 69 | handler: print/print.handler 70 | description: Generates a PDF from HTML (using Chromium v122) 71 | memorySize: 2048 72 | timeout: 30 73 | layers: 74 | - { Ref: HeadlessChromiumLambdaLayer } 75 | events: 76 | - http: 77 | path: /print 78 | method: POST 79 | cors: ${self:custom.cors} 80 | 81 | serviceRead: 82 | handler: service/version.handler 83 | description: Reads service metadata 84 | events: 85 | - http: 86 | method: GET 87 | path: service/version 88 | cors: ${self:custom.cors} 89 | 90 | resources: 91 | Description: API for generating PDF from HTML 92 | 93 | plugins: 94 | - serverless-webpack 95 | - serverless-apigw-binary 96 | -------------------------------------------------------------------------------- /packages/api/service/test/version.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /packages/api/service/version.js: -------------------------------------------------------------------------------- 1 | const packageJson = require('./../package.json'); 2 | 3 | const LambdaHelper = require('./../common/aws/LambdaHelper'); 4 | 5 | module.exports = (() => { 6 | 'use strict'; 7 | 8 | return { 9 | handler: (event, context, callback) => { 10 | LambdaHelper.process('Read service metadata', event, callback, (parser, responder) => { 11 | return { 12 | service: { 13 | name: packageJson.name, 14 | description: packageJson.description, 15 | environment: process.env.NODE_ENV, 16 | version: packageJson.version 17 | } 18 | }; 19 | }); 20 | } 21 | }; 22 | })(); 23 | 24 | 25 | -------------------------------------------------------------------------------- /packages/api/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'), 2 | slsw = require('serverless-webpack'); 3 | 4 | module.exports = { 5 | mode: 'production', 6 | entry: slsw.lib.entries, 7 | devtool: 'source-map', 8 | resolve: { 9 | mainFields: ['main', 'module'], 10 | extensions: ['.wasm', '.js', '.mjs', '.json'] 11 | }, 12 | plugins: [ 13 | new webpack.ContextReplacementPlugin(/\/log4js\/lib\/appenders/, (data) => { 14 | delete data.dependencies[0].critical; 15 | 16 | return data; 17 | }), 18 | new webpack.ContextReplacementPlugin(/\/yargs(-parser)?\/build/, (data) => { 19 | data.dependencies.forEach((dependency) => { 20 | delete dependency.critical; 21 | }); 22 | 23 | return data; 24 | }) 25 | ], 26 | externals: { 27 | '@sparticuz/chromium': '@sparticuz/chromium', 28 | 'bufferutil': 'bufferutil', 29 | 'utf-8-validate': 'utf-8-validate' 30 | } 31 | }; 32 | --------------------------------------------------------------------------------