Uses Puppeteer to render a copy of the user's timetable on the server before taking a screenshot and sending it back to them.
# Install dependencies
# To skip Puppeteer installing Chromium, set PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
# See https://github.com/GoogleChrome/puppeteer/blob/v0.13.0/docs/api.md#environment-variables
$ yarn
# Configure - the defaults are sufficient for development, but for
# production these need to be updated
$ cp config.example.js config.js
$ vim config.js
# Start the Webpack server for both the main web app and the standalone
# timetable page used by the export service
cd ../website
yarn start
yarn start:export
# Start export server - use "yarn devtools" if need to see the graphical browser with
# developer tools. Note that PDF export does not work in devtools mode.
cd ../export
yarn dev
- Dark mode
- Timetable orientation
- Hidden modules
- Custom coloring
- The user's client extracts the relevant slice of the Redux state necessary to render the timetable and serializes it into JSON
- The serialized state is added as query params to the 'Download as' link the user sees when they open the dropdown menu
- When the user clicks on the link, the browser makes a GET request to the chosen export endpoint
- The endpoint parses and validates the incoming data
- The export service uses the Puppeteer instance to create a new
Page
object - The page loads a special version of the app that only has the
<TimetableContent>
component, and injects the data into the Redux store - The screenshot or PDF is taken of the page, and sent back to the user
Both export API endpoints accept GET requests and take a data
query param that consists of JSON encoded data in the shape of ExportData
.
The endpoints use content-disposition
header to get the browser to download the file.
Download PDF of the timetable.
data
- JSON encoded timetable data
Download PNG image of the timetable.
data
- JSON encoded timetable datapixelRatio = 1
(Number: 1 to 3 inclusive) - the device pixel ratio as reported inwindow.devicePixelRatio
. This scales the entire image by that amount so it will appear correctly when downloaded to the user's device.
Returns the HTML content of the page that Puppeteer renders.
In production the app runs behind forever in addition to nodemon, so the app will automatically restart when its files are changed or if the app crashes.
Export depends on build artifacts from website
. The deploy script for website
will automatically push updated versions of the artifacts to the correct folder, but staging needs to be updated manually. Use yarn rsync:export ../exports/dist-timetable
from the website
folder in staging to do this.
Here are the steps for deploying. We rsync everything over, including the node_modules
, so it is not necesary to run yarn
in the production folder.
# Update the files from the repo
$ git pull
# Install dependencies
$ yarn
# Deploy the files to production - optionally add --dry-run to check which files are changed first
$ yarn deploy
# Starting the app, if the app is not already running
# Use port 3300 for production and 3301 for staging
$ cd ../../nusmods-export
$ PORT=3300 NODE_ENV=production yarn start
# Check that the app is running - also monitor Sentry for errors
$ forever list
$ forever logs <index>
# Stopping the app - it may be necessary to manually kill the node and chrome processes
# if they did not manage to shutdown properly
$ forever stop <index>