Skip to content

Commit

Permalink
feat: proxy for mapbox (#730)
Browse files Browse the repository at this point in the history
Co-authored-by: Eric Pedley <[email protected]>
  • Loading branch information
ap0nia and EricPedley authored Oct 16, 2023
1 parent 38bf45e commit 223238f
Show file tree
Hide file tree
Showing 11 changed files with 91 additions and 76 deletions.
1 change: 1 addition & 0 deletions .github/workflows/step_setup_and_build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 8 additions & 5 deletions apps/antalmanac/src/components/Map/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,11 @@ 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 =
'&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors | Images from <a href="https://map.uci.edu/?id=463">UCI Map</a>';

const url = `https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token=${ACCESS_TOKEN}`;

const WORK_WEEK = ['All', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri'];
const FULL_WEEK = ['All', 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const weekendIndices = [0, 6];
Expand Down Expand Up @@ -231,7 +228,13 @@ export default function CourseMap() {
/>
</Paper>

<TileLayer attribution={ATTRIBUTION_MARKUP} url={url} tileSize={512} maxZoom={21} zoomOffset={-1} />
<TileLayer
attribution={ATTRIBUTION_MARKUP}
url={`${MAPBOX_PROXY_TILES_ENDPOINT}/{z}/{x}/{y}`}
tileSize={512}
maxZoom={21}
zoomOffset={-1}
/>

<UserLocator />

Expand Down
7 changes: 4 additions & 3 deletions apps/antalmanac/src/components/Map/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
/**
Expand Down Expand Up @@ -40,12 +39,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: MAPBOX_PROXY_DIRECTIONS_ENDPOINT,
}),

/**
Expand Down
4 changes: 3 additions & 1 deletion apps/antalmanac/src/lib/api/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`
Expand All @@ -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';
Expand Down
3 changes: 3 additions & 0 deletions apps/backend/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions apps/backend/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
'|>',
Expand Down
25 changes: 24 additions & 1 deletion apps/backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ 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'],
};

const MAPBOX_API_URL = 'https://api.mapbox.com';

const PORT = 3000;

export async function start(corsEnabled = false) {
Expand All @@ -20,6 +22,27 @@ export async function start(corsEnabled = false) {
app.use(cors(corsEnabled ? corsOptions : undefined));
app.use(express.json());

app.use('/mapbox/directions/*', async (req, res) => {
const searchParams = new URLSearchParams(req.query as any);
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);
});

app.use('/mapbox/tiles/*', async (req, res) => {
const searchParams = new URLSearchParams(req.query as any);
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 buffer = await fetch(url).then((res) => res.arrayBuffer());
res.type('image/png')
res.send(Buffer.from(buffer))
// // res.header('Content-Security-Policy', "img-src 'self'"); // https://stackoverflow.com/questions/56386307/loading-of-a-resource-blocked-by-content-security-policy
// // res.header('Access-Control-Allow-Methods', 'GET, OPTIONS')
// res.type('image/png')
// res.send(result)
});

app.use(
'/trpc',
createExpressMiddleware({
Expand Down
3 changes: 2 additions & 1 deletion apps/cdk/.env.sample
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
MONGODB_URI_DEV=dev uri
MONGODB_URI_PROD=prod uri
MONGODB_URI_PROD=prod uri
MAPBOX_ACCESS_TOKEN=pk.abc123
38 changes: 14 additions & 24 deletions apps/cdk/lib/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
)
})
}
}

Expand All @@ -60,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') {
Expand Down
71 changes: 30 additions & 41 deletions apps/cdk/lib/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)

Expand All @@ -79,19 +73,14 @@ export default class BackendStack extends Stack {
),
endpointType: apigateway.EndpointType.EDGE,
},
binaryMediaTypes: ['image/*'],
},
)

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)),
})
}
}

0 comments on commit 223238f

Please sign in to comment.