From 70b2a221f2fe1946300b3aee914e60e2bb91673d Mon Sep 17 00:00:00 2001 From: Aponia Date: Thu, 12 Oct 2023 12:55:11 -0700 Subject: [PATCH 1/6] feat: proxy for mapbox --- apps/antalmanac/src/components/Map/Routes.tsx | 6 ++++-- apps/backend/src/index.ts | 12 ++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/apps/antalmanac/src/components/Map/Routes.tsx b/apps/antalmanac/src/components/Map/Routes.tsx index 81ee659cd..4e4aea8c2 100644 --- a/apps/antalmanac/src/components/Map/Routes.tsx +++ b/apps/antalmanac/src/components/Map/Routes.tsx @@ -5,7 +5,7 @@ import 'leaflet-routing-machine'; import { createElementHook, createElementObject, useLeafletContext } from '@react-leaflet/core'; import type { LeafletContextInterface } from '@react-leaflet/core'; -const ACCESS_TOKEN = 'pk.eyJ1IjoicGVkcmljIiwiYSI6ImNsZzE0bjk2ajB0NHEzanExZGFlbGpwazIifQ.l14rgv5vmu5wIMgOUUhUXw'; +// const ACCESS_TOKEN = 'pk.eyJ1IjoicGVkcmljIiwiYSI6ImNsZzE0bjk2ajB0NHEzanExZGFlbGpwazIifQ.l14rgv5vmu5wIMgOUUhUXw'; interface ClassRoutesProps { /** @@ -40,12 +40,14 @@ function createRouter(props: ClassRoutesProps, context: LeafletContextInterface) const waypoints = latLngTuples.map((latLngTuple) => L.latLng(latLngTuple)); const routerControl = L.Routing.control({ - router: L.Routing.mapbox(ACCESS_TOKEN, { + router: L.Routing.mapbox('', { /** * Default is mapbox/driving. More options: * @see {@link https://docs.mapbox.com/api/navigation/directions/#routing-profiles} */ profile: 'mapbox/walking', + + serviceUrl: 'http://localhost:3000/mapbox/directions', }), /** diff --git a/apps/backend/src/index.ts b/apps/backend/src/index.ts index be9483e82..acf5fa2cf 100644 --- a/apps/backend/src/index.ts +++ b/apps/backend/src/index.ts @@ -11,6 +11,10 @@ const corsOptions: CorsOptions = { origin: ['https://antalmanac.com', 'https://www.antalmanac.com', 'https://icssc-projects.github.io/AntAlmanac'], }; +const MAPBOX_ACCESS_TOKEN = 'pk.eyJ1IjoicGVkcmljIiwiYSI6ImNsZzE0bjk2ajB0NHEzanExZGFlbGpwazIifQ.l14rgv5vmu5wIMgOUUhUXw'; + +const MAPBOX_API_URL = 'https://api.mapbox.com'; + const PORT = 3000; export async function start(corsEnabled = false) { @@ -20,6 +24,14 @@ export async function start(corsEnabled = false) { app.use(cors(corsEnabled ? corsOptions : undefined)); app.use(express.json()); + app.use('/mapbox/directions/*', async (req, res, next) => { + const searchParams = new URLSearchParams(req.query as any); + searchParams.set('access_token', MAPBOX_ACCESS_TOKEN); + const url = `${MAPBOX_API_URL}/directions/v5/${req.params[0]}?${searchParams.toString()}`; + const result = await fetch(url).then((res) => res.text()); + res.send(result); + }); + app.use( '/trpc', createExpressMiddleware({ From 76d0ef91e9ac55f70462a68d472bc24c6409a49f Mon Sep 17 00:00:00 2001 From: Eric Pedley Date: Thu, 12 Oct 2023 13:39:38 -0700 Subject: [PATCH 2/6] move all mapbox requests to backend proxy --- apps/antalmanac/src/components/Map/Map.tsx | 5 ++--- apps/antalmanac/src/components/Map/Routes.tsx | 5 ++--- apps/antalmanac/src/lib/api/endpoints.ts | 4 +++- apps/backend/src/index.ts | 16 ++++++++++++++-- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/apps/antalmanac/src/components/Map/Map.tsx b/apps/antalmanac/src/components/Map/Map.tsx index 2f33e5715..5ec52f9a6 100644 --- a/apps/antalmanac/src/components/Map/Map.tsx +++ b/apps/antalmanac/src/components/Map/Map.tsx @@ -14,13 +14,12 @@ import locationIds from '$lib/location_ids'; import buildingCatalogue from '$lib/buildingCatalogue'; import type { Building } from '$lib/buildingCatalogue'; import type { CourseEvent } from '$components/Calendar/CourseCalendarEvent'; - -const ACCESS_TOKEN = 'pk.eyJ1IjoicGVkcmljIiwiYSI6ImNsZzE0bjk2ajB0NHEzanExZGFlbGpwazIifQ.l14rgv5vmu5wIMgOUUhUXw'; +import { MAPBOX_PROXY_TILES_ENDPOINT } from '$lib/api/endpoints'; const ATTRIBUTION_MARKUP = '© OpenStreetMap contributors | Images from UCI Map'; -const url = `https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token=${ACCESS_TOKEN}`; +const url = `${MAPBOX_PROXY_TILES_ENDPOINT}/{z}/{x}/{y}`; const WORK_WEEK = ['All', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri']; const FULL_WEEK = ['All', 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; diff --git a/apps/antalmanac/src/components/Map/Routes.tsx b/apps/antalmanac/src/components/Map/Routes.tsx index 4e4aea8c2..597e15f83 100644 --- a/apps/antalmanac/src/components/Map/Routes.tsx +++ b/apps/antalmanac/src/components/Map/Routes.tsx @@ -4,8 +4,7 @@ import type { LatLngTuple } from 'leaflet'; import 'leaflet-routing-machine'; import { createElementHook, createElementObject, useLeafletContext } from '@react-leaflet/core'; import type { LeafletContextInterface } from '@react-leaflet/core'; - -// const ACCESS_TOKEN = 'pk.eyJ1IjoicGVkcmljIiwiYSI6ImNsZzE0bjk2ajB0NHEzanExZGFlbGpwazIifQ.l14rgv5vmu5wIMgOUUhUXw'; +import { MAPBOX_PROXY_DIRECTIONS_ENDPOINT } from '$lib/api/endpoints'; interface ClassRoutesProps { /** @@ -47,7 +46,7 @@ function createRouter(props: ClassRoutesProps, context: LeafletContextInterface) */ profile: 'mapbox/walking', - serviceUrl: 'http://localhost:3000/mapbox/directions', + serviceUrl: MAPBOX_PROXY_DIRECTIONS_ENDPOINT, }), /** diff --git a/apps/antalmanac/src/lib/api/endpoints.ts b/apps/antalmanac/src/lib/api/endpoints.ts index 3cb2298e0..673b2cae8 100644 --- a/apps/antalmanac/src/lib/api/endpoints.ts +++ b/apps/antalmanac/src/lib/api/endpoints.ts @@ -4,7 +4,7 @@ function endpointTransform(path: string) { return `https://${import.meta.env.VITE_ENDPOINT}.api.antalmanac.com${path}`; } if (import.meta.env.VITE_LOCAL_SERVER) { - return `http://localhost:8080${path}`; + return `http://localhost:3000${path}`; } return import.meta.env.MODE === 'development' ? `https://dev.api.antalmanac.com${path}` @@ -13,6 +13,8 @@ function endpointTransform(path: string) { export const LOOKUP_NOTIFICATIONS_ENDPOINT = endpointTransform('/api/notifications/lookupNotifications'); export const REGISTER_NOTIFICATIONS_ENDPOINT = endpointTransform('/api/notifications/registerNotifications'); +export const MAPBOX_PROXY_DIRECTIONS_ENDPOINT = endpointTransform('/mapbox/directions'); +export const MAPBOX_PROXY_TILES_ENDPOINT = endpointTransform('/mapbox/tiles'); // PeterPortal API export const PETERPORTAL_GRAPHQL_ENDPOINT = 'https://api-next.peterportal.org/v1/graphql'; diff --git a/apps/backend/src/index.ts b/apps/backend/src/index.ts index acf5fa2cf..8affa6cce 100644 --- a/apps/backend/src/index.ts +++ b/apps/backend/src/index.ts @@ -4,8 +4,8 @@ import type { CorsOptions } from 'cors'; import { createExpressMiddleware } from '@trpc/server/adapters/express'; import AppRouter from './routers'; import createContext from './context'; -import connectToMongoDB from '$db/mongodb'; import env from './env'; +import connectToMongoDB from '$db/mongodb'; const corsOptions: CorsOptions = { origin: ['https://antalmanac.com', 'https://www.antalmanac.com', 'https://icssc-projects.github.io/AntAlmanac'], @@ -24,7 +24,7 @@ export async function start(corsEnabled = false) { app.use(cors(corsEnabled ? corsOptions : undefined)); app.use(express.json()); - app.use('/mapbox/directions/*', async (req, res, next) => { + app.use('/mapbox/directions/*', async (req, res) => { const searchParams = new URLSearchParams(req.query as any); searchParams.set('access_token', MAPBOX_ACCESS_TOKEN); const url = `${MAPBOX_API_URL}/directions/v5/${req.params[0]}?${searchParams.toString()}`; @@ -32,6 +32,18 @@ export async function start(corsEnabled = false) { res.send(result); }); + app.use('/mapbox/tiles/*', async (req, res) => { + console.log(req.params[0]) + const searchParams = new URLSearchParams(req.query as any); + searchParams.set('access_token', MAPBOX_ACCESS_TOKEN); + const url = `${MAPBOX_API_URL}/styles/v1/mapbox/streets-v11/tiles/${req.params[0]}?${searchParams.toString()}`; + const result = await fetch(url).then((res) => res.blob()); + res.type(result.type) + result.arrayBuffer().then((buf) => { + res.send(Buffer.from(buf)) + }); + }); + app.use( '/trpc', createExpressMiddleware({ From 0eb7f452dbe43da3930f47f41daab4556e6dd007 Mon Sep 17 00:00:00 2001 From: Eric Pedley Date: Thu, 12 Oct 2023 13:40:01 -0700 Subject: [PATCH 3/6] get rid of unnecessary console log --- apps/backend/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/backend/src/index.ts b/apps/backend/src/index.ts index 8affa6cce..888097607 100644 --- a/apps/backend/src/index.ts +++ b/apps/backend/src/index.ts @@ -33,7 +33,6 @@ export async function start(corsEnabled = false) { }); app.use('/mapbox/tiles/*', async (req, res) => { - console.log(req.params[0]) const searchParams = new URLSearchParams(req.query as any); searchParams.set('access_token', MAPBOX_ACCESS_TOKEN); const url = `${MAPBOX_API_URL}/styles/v1/mapbox/streets-v11/tiles/${req.params[0]}?${searchParams.toString()}`; From 3a2d08eb225c3464433af0e5682c8fd36ac4e417 Mon Sep 17 00:00:00 2001 From: Eric Pedley Date: Thu, 12 Oct 2023 13:41:56 -0700 Subject: [PATCH 4/6] move mapbox token to env vars --- apps/backend/src/env.ts | 1 + apps/backend/src/index.ts | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/backend/src/env.ts b/apps/backend/src/env.ts index 53ba01944..a1e7db140 100644 --- a/apps/backend/src/env.ts +++ b/apps/backend/src/env.ts @@ -7,6 +7,7 @@ const Environment = type([ USERDATA_TABLE_NAME: 'string', AA_MONGODB_URI: 'string', AWS_REGION: 'string', + MAPBOX_ACCESS_TOKEN: 'string', 'PR_NUM?': 'number', }, '|>', diff --git a/apps/backend/src/index.ts b/apps/backend/src/index.ts index 888097607..d94d02db6 100644 --- a/apps/backend/src/index.ts +++ b/apps/backend/src/index.ts @@ -11,8 +11,6 @@ const corsOptions: CorsOptions = { origin: ['https://antalmanac.com', 'https://www.antalmanac.com', 'https://icssc-projects.github.io/AntAlmanac'], }; -const MAPBOX_ACCESS_TOKEN = 'pk.eyJ1IjoicGVkcmljIiwiYSI6ImNsZzE0bjk2ajB0NHEzanExZGFlbGpwazIifQ.l14rgv5vmu5wIMgOUUhUXw'; - const MAPBOX_API_URL = 'https://api.mapbox.com'; const PORT = 3000; @@ -26,7 +24,7 @@ export async function start(corsEnabled = false) { app.use('/mapbox/directions/*', async (req, res) => { const searchParams = new URLSearchParams(req.query as any); - searchParams.set('access_token', MAPBOX_ACCESS_TOKEN); + searchParams.set('access_token', env.MAPBOX_ACCESS_TOKEN); const url = `${MAPBOX_API_URL}/directions/v5/${req.params[0]}?${searchParams.toString()}`; const result = await fetch(url).then((res) => res.text()); res.send(result); @@ -34,7 +32,7 @@ export async function start(corsEnabled = false) { app.use('/mapbox/tiles/*', async (req, res) => { const searchParams = new URLSearchParams(req.query as any); - searchParams.set('access_token', MAPBOX_ACCESS_TOKEN); + searchParams.set('access_token', env.MAPBOX_ACCESS_TOKEN); const url = `${MAPBOX_API_URL}/styles/v1/mapbox/streets-v11/tiles/${req.params[0]}?${searchParams.toString()}`; const result = await fetch(url).then((res) => res.blob()); res.type(result.type) From 7fc824e852fac9c097e3f5b4d9c536e9808ede32 Mon Sep 17 00:00:00 2001 From: Eric Pedley Date: Thu, 12 Oct 2023 13:50:08 -0700 Subject: [PATCH 5/6] add mapbox token env secret stuff to devops workflow --- .github/workflows/step_setup_and_build.yaml | 1 + .github/workflows/test.yaml | 1 + apps/backend/.env.sample | 3 + apps/cdk/.env.sample | 3 +- apps/cdk/lib/app.ts | 37 ++++------- apps/cdk/lib/backend.ts | 70 +++++++++------------ 6 files changed, 49 insertions(+), 66 deletions(-) diff --git a/.github/workflows/step_setup_and_build.yaml b/.github/workflows/step_setup_and_build.yaml index a584c741f..ebc336bd7 100644 --- a/.github/workflows/step_setup_and_build.yaml +++ b/.github/workflows/step_setup_and_build.yaml @@ -64,6 +64,7 @@ jobs: HOSTED_ZONE_ID: ${{ secrets.HOSTED_ZONE_ID }} CERTIFICATE_ARN: ${{ secrets.CERTIFICATE_ARN }} MONGODB_URI_PROD: ${{ secrets.MONGODB_URI_PROD }} + MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN }} PR_NUM: ${{ github.event.pull_request.number }} # cdk env variables API_SUB_DOMAIN: ${{ env.apiSubDomain }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index d72a3a765..a70b7b362 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -51,6 +51,7 @@ jobs: HOSTED_ZONE_ID: ${{ secrets.HOSTED_ZONE_ID }} CERTIFICATE_ARN: ${{ secrets.CERTIFICATE_ARN }} MONGODB_URI_PROD: ${{ secrets.MONGODB_URI_PROD }} + MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN }} PR_NUM: ${{ github.event.pull_request.number }} # CDK environment variables. diff --git a/apps/backend/.env.sample b/apps/backend/.env.sample index f76d05f0d..c935e3941 100644 --- a/apps/backend/.env.sample +++ b/apps/backend/.env.sample @@ -9,3 +9,6 @@ USERDATA_TABLE_NAME=tablename # Provided by Lambda when running on AWS. AWS_REGION=us-east-1 + +# For Mapbox API +MAPBOX_ACCESS_TOKEN=pk.abc123 \ No newline at end of file diff --git a/apps/cdk/.env.sample b/apps/cdk/.env.sample index 5983407c2..7ef2814d6 100644 --- a/apps/cdk/.env.sample +++ b/apps/cdk/.env.sample @@ -1,2 +1,3 @@ MONGODB_URI_DEV=dev uri -MONGODB_URI_PROD=prod uri \ No newline at end of file +MONGODB_URI_PROD=prod uri +MAPBOX_ACCESS_TOKEN=pk.abc123 \ No newline at end of file diff --git a/apps/cdk/lib/app.ts b/apps/cdk/lib/app.ts index 1e345a641..5d3f07a64 100644 --- a/apps/cdk/lib/app.ts +++ b/apps/cdk/lib/app.ts @@ -6,41 +6,30 @@ import FrontendStack from './frontend' const app = new App({ autoSynth: true }) // Check environmental variables -if ( - !process.env.CERTIFICATE_ARN || - !process.env.HOSTED_ZONE_ID || - !process.env.MONGODB_URI_PROD -) { +if (!process.env.CERTIFICATE_ARN || !process.env.HOSTED_ZONE_ID || !process.env.MONGODB_URI_PROD) { throw new Error('Missing environmental variables') } // Deploy staging if (process.env.PR_NUM) { const env: Environment = { region: 'us-east-1' } - new FrontendStack( - app, - `antalmanac-frontend-staging-${process.env.PR_NUM}`, - { + new FrontendStack(app, `antalmanac-frontend-staging-${process.env.PR_NUM}`, { + env, + stage: 'staging', + certificateArn: process.env.CERTIFICATE_ARN, + hostedZoneId: process.env.HOSTED_ZONE_ID, + prNum: process.env.PR_NUM, + }) + if (process.env.apiSubDomain !== 'dev') { + new BackendStack(app, `antalmanac-backend-staging-${process.env.PR_NUM}`, { env, stage: 'staging', certificateArn: process.env.CERTIFICATE_ARN, hostedZoneId: process.env.HOSTED_ZONE_ID, + mongoDbUriProd: process.env.MONGODB_URI_PROD, + mapboxAccessToken: process.env.MAPBOX_ACCESS_TOKEN, prNum: process.env.PR_NUM, - }, - ) - if (process.env.apiSubDomain !== 'dev') { - new BackendStack( - app, - `antalmanac-backend-staging-${process.env.PR_NUM}`, - { - env, - stage: 'staging', - certificateArn: process.env.CERTIFICATE_ARN, - hostedZoneId: process.env.HOSTED_ZONE_ID, - mongoDbUriProd: process.env.MONGODB_URI_PROD, - prNum: process.env.PR_NUM, - }, - ) + }) } } diff --git a/apps/cdk/lib/backend.ts b/apps/cdk/lib/backend.ts index 39e019a45..715f8954e 100644 --- a/apps/cdk/lib/backend.ts +++ b/apps/cdk/lib/backend.ts @@ -11,6 +11,7 @@ import { transformUrl } from './helpers' export interface BackendProps extends StackProps { stage: string mongoDbUriProd: string + mapboxAccessToken: string hostedZoneId: string certificateArn: string prNum?: string @@ -20,39 +21,32 @@ export default class BackendStack extends Stack { constructor(scope: Construct, id: string, props: BackendProps) { super(scope, id, props) - const userDataDDB = new dynamnodb.Table( - this, - `antalmanac-userdata-ddb-${props.stage}`, - { - partitionKey: { - name: 'id', - type: dynamnodb.AttributeType.STRING, - }, - billingMode: dynamnodb.BillingMode.PAY_PER_REQUEST, - removalPolicy: - props.stage === 'dev' || props.stage === 'prod' - ? RemovalPolicy.RETAIN - : RemovalPolicy.DESTROY, + const userDataDDB = new dynamnodb.Table(this, `antalmanac-userdata-ddb-${props.stage}`, { + partitionKey: { + name: 'id', + type: dynamnodb.AttributeType.STRING, }, - ) + billingMode: dynamnodb.BillingMode.PAY_PER_REQUEST, + removalPolicy: + props.stage === 'dev' || props.stage === 'prod' + ? RemovalPolicy.RETAIN + : RemovalPolicy.DESTROY, + }) - const api = new lambda.Function( - this, - `antalmanac-api-${props.stage}-lambda`, - { - runtime: lambda.Runtime.NODEJS_18_X, - code: lambda.Code.fromAsset('../backend/dist'), - handler: 'lambda.handler', - timeout: Duration.seconds(5), - memorySize: 256, - environment: { - // We don't need dev database because we will never write to it - AA_MONGODB_URI: props.mongoDbUriProd, - STAGE: props.stage, - USERDATA_TABLE_NAME: userDataDDB.tableName, - }, + const api = new lambda.Function(this, `antalmanac-api-${props.stage}-lambda`, { + runtime: lambda.Runtime.NODEJS_18_X, + code: lambda.Code.fromAsset('../backend/dist'), + handler: 'lambda.handler', + timeout: Duration.seconds(5), + memorySize: 256, + environment: { + // We don't need dev database because we will never write to it + AA_MONGODB_URI: props.mongoDbUriProd, + STAGE: props.stage, + USERDATA_TABLE_NAME: userDataDDB.tableName, + MAPBOX_ACCESS_TOKEN: props.mapboxAccessToken, }, - ) + }) userDataDDB.grantReadWriteData(api) @@ -82,16 +76,10 @@ export default class BackendStack extends Stack { }, ) - new route53.ARecord( - this, - `antalmanac-backend-a-record-${props.stage}`, - { - zone: zone, - recordName: transformUrl('api', props), - target: route53.RecordTarget.fromAlias( - new targets.ApiGateway(apiGateway), - ), - }, - ) + new route53.ARecord(this, `antalmanac-backend-a-record-${props.stage}`, { + zone: zone, + recordName: transformUrl('api', props), + target: route53.RecordTarget.fromAlias(new targets.ApiGateway(apiGateway)), + }) } } From 7f441f62bfe538b47d1bfa94207a5dd2187a7791 Mon Sep 17 00:00:00 2001 From: Eric Pedley Date: Thu, 12 Oct 2023 13:54:58 -0700 Subject: [PATCH 6/6] fix missing mapbox token env var --- apps/cdk/lib/app.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/cdk/lib/app.ts b/apps/cdk/lib/app.ts index 5d3f07a64..65897253a 100644 --- a/apps/cdk/lib/app.ts +++ b/apps/cdk/lib/app.ts @@ -49,6 +49,7 @@ else { certificateArn: process.env.CERTIFICATE_ARN, hostedZoneId: process.env.HOSTED_ZONE_ID, mongoDbUriProd: process.env.MONGODB_URI_PROD, + mapboxAccessToken: process.env.MAPBOX_ACCESS_TOKEN, }) // prod frontend is deployed on GitHub Pages if (stage !== 'prod') {