Skip to content

Commit

Permalink
Pong2 (#9775)
Browse files Browse the repository at this point in the history
* feat(cloud-function): ad bsa support

this refactors the pong lib:
- bundle click.js, get.js, viewed.js in pong.js
- add pong2.js based on bsa's api
- remove fallback

* add stage config

* remove log

* decode copy to be safe

* feedback
  • Loading branch information
fiji-flo authored Oct 25, 2023
1 parent abb0b35 commit 4d9413d
Show file tree
Hide file tree
Showing 23 changed files with 452 additions and 323 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/stage-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -347,15 +347,16 @@ jobs:
--set-env-vars="ORIGIN_PLAY=mdnyalp.dev" \
--set-env-vars="SOURCE_CONTENT=https://storage.googleapis.com/${{ vars.GCP_BUCKET_NAME }}/main/" \
--set-env-vars="SOURCE_API=https://api.developer.allizom.org/" \
--set-env-vars="BSA_ENABLED=true" \
--set-env-vars="BSA_URL_PREFIX=https://developer.allizom.org/fr/" \
--set-env-vars="SENTRY_DSN=${{ secrets.SENTRY_DSN_CLOUD_FUNCTION }}" \
--set-env-vars="SENTRY_ENVIRONMENT=stage" \
--set-env-vars="SENTRY_TRACES_SAMPLE_RATE=${{ vars.SENTRY_TRACES_SAMPLE_RATE }}" \
--set-env-vars="SENTRY_RELEASE=${{ github.sha }}" \
--set-secrets="KEVEL_SITE_ID=projects/${{ secrets.GCP_PROJECT_NAME }}/secrets/stage-kevel-site-id/versions/latest" \
--set-secrets="KEVEL_NETWORK_ID=projects/${{ secrets.GCP_PROJECT_NAME }}/secrets/stage-kevel-network-id/versions/latest" \
--set-secrets="SIGN_SECRET=projects/${{ secrets.GCP_PROJECT_NAME }}/secrets/stage-sign-secret/versions/latest" \
--set-secrets="CARBON_ZONE_KEY=projects/${{ secrets.GCP_PROJECT_NAME }}/secrets/stage-carbon-zone-key/versions/latest" \
--set-secrets="CARBON_FALLBACK_ENABLED=projects/${{ secrets.GCP_PROJECT_NAME }}/secrets/stage-fallback-enabled/versions/latest" \
--set-secrets="BSA_ZONE_KEYS=projects/${{ secrets.GCP_PROJECT_NAME }}/secrets/stage-bsa-zone-keys/versions/latest" \
2>&1 | sed "s/^/[$region] /" &
pids+=($!)
done
Expand Down
1 change: 1 addition & 0 deletions client/src/placement-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export interface PlacementData {
ctaTextColorDark?: string;
ctaBackgroundColorDark?: string;
};
version?: number;
}

type PlacementType = "side" | "top" | "hpMain" | "hpFooter";
Expand Down
13 changes: 8 additions & 5 deletions client/src/ui/organisms/placement/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ interface PlacementRenderArgs {
cta?: string;
user: User;
style: object;
version?: number;
}

const INTERSECTION_OPTIONS = {
Expand All @@ -42,9 +43,7 @@ function viewed(pong?: PlacementData) {
pong?.view &&
navigator.sendBeacon?.(
`/pong/viewed?code=${encodeURIComponent(pong?.view)}${
pong?.fallback
? `&fallback=${encodeURIComponent(pong?.fallback?.view)}`
: ""
pong?.version ? `&version=${pong.version}` : ""
}`
);
}
Expand Down Expand Up @@ -262,7 +261,7 @@ export function PlacementInner({
}, [isVisible, isIntersecting, sendViewed]);

const { image, copy } = pong?.fallback || pong || {};
const { click } = pong || {};
const { click, version } = pong || {};
return (
<>
{!isServer &&
Expand All @@ -278,6 +277,7 @@ export function PlacementInner({
cta,
user,
style,
version,
})}
</>
);
Expand All @@ -294,6 +294,7 @@ function RenderSideOrTopBanner({
cta,
user,
style,
version = 1,
}: PlacementRenderArgs) {
return (
<section
Expand All @@ -305,7 +306,9 @@ function RenderSideOrTopBanner({
<a
className="pong"
data-glean="pong: pong->click"
href={`/pong/click?code=${encodeURIComponent(click)}`}
href={`/pong/click?code=${encodeURIComponent(
click
)}&version=${version}`}
target="_blank"
rel="noreferrer"
>
Expand Down
9 changes: 5 additions & 4 deletions cloud-function/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@ The placement handler uses the following environment variables:
- `KEVEL_SITE_ID` (default: `0`) - Required for serving placements via Kevel.
- `KEVEL_NETWORK_ID` (default: `0`) - Required for serving placements via Kevel.
- `SIGN_SECRET` (default: `""`) - Required for serving placements.
- `CARBON_ZONE_KEY` (default: `""`) - Required for serving placements via
Carbon.
- `CARBON_FALLBACK_ENABLED` (default: `"false"`) - Whether fallback placements
should be served via Carbon.
- `BSA_ZONE_KEYS` (default: `""`) - Required for serving placements via BSA.
- `BSA_URL_PREFIX`(default: "https://localhost") - Where to show BSA placements
if enabled. Formatted like :
"placementname1:zonekey1;placementkey2:zonekey2...".
- `BSA_ENABLED` (default: `"false"`) - Whether to use placements via BSA.

You can override the defaults by adding a `.env` file with `KEY=value` lines.
6 changes: 3 additions & 3 deletions cloud-function/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { ANY_ATTACHMENT_EXT } from "./internal/constants/index.js";

import { Origin } from "./env.js";
import { proxyContent } from "./handlers/proxy-content.js";
import { proxyKevel } from "./handlers/proxy-kevel.js";
import { proxyApi } from "./handlers/proxy-api.js";
import { handleStripePlans } from "./handlers/handle-stripe-plans.js";
import { proxyTelemetry } from "./handlers/proxy-telemetry.js";
Expand All @@ -22,6 +21,7 @@ import { notFound } from "./middlewares/not-found.js";
import { resolveRunnerHtml } from "./middlewares/resolve-runner-html.js";
import { proxyRunner } from "./handlers/proxy-runner.js";
import { stripForwardedHostHeaders } from "./middlewares/stripForwardedHostHeaders.js";
import { proxyPong } from "./handlers/proxy-pong.js";

const router = Router();
router.use(stripForwardedHostHeaders);
Expand All @@ -37,8 +37,8 @@ router.all(
proxyApi
);
router.all("/submit/mdn-yari/*", requireOrigin(Origin.main), proxyTelemetry);
router.all("/pong/*", requireOrigin(Origin.main), express.json(), proxyKevel);
router.all("/pimg/*", requireOrigin(Origin.main), proxyKevel);
router.all("/pong/*", requireOrigin(Origin.main), express.json(), proxyPong);
router.all("/pimg/*", requireOrigin(Origin.main), proxyPong);
router.get(
["/[^/]+/docs/*/runner.html", "/[^/]+/blog/*/runner.html", "/runner.html"],
requireOrigin(Origin.play),
Expand Down
10 changes: 7 additions & 3 deletions cloud-function/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,13 @@ export function sourceUri(source: Source): string {
export const KEVEL_SITE_ID = Number(process.env["KEVEL_SITE_ID"] ?? 0);
export const KEVEL_NETWORK_ID = Number(process.env["KEVEL_NETWORK_ID"] ?? 0);
export const SIGN_SECRET = process.env["SIGN_SECRET"] ?? "";
export const CARBON_ZONE_KEY = process.env["CARBON_ZONE_KEY"] ?? "";
export const CARBON_FALLBACK_ENABLED = Boolean(
JSON.parse(process.env["CARBON_FALLBACK_ENABLED"] || "false")
export const BSA_ZONE_KEYS = Object.fromEntries(
(process.env["BSA_ZONE_KEYS"] ?? "").split(";").map((k) => k.split(":"))
);
export const BSA_URL_PREFIX =
process.env["BSA_URL_PREFIX"] ?? "https://localhost";
export const BSA_ENABLED = Boolean(
JSON.parse(process.env["BSA_ENABLED"] || "false")
);

// HTTPS.
Expand Down
94 changes: 94 additions & 0 deletions cloud-function/src/handlers/proxy-bsa.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import * as url from "node:url";

import type { Request, Response } from "express";

import { Coder } from "../internal/pong/index.js";
import {
createPong2GetHandler,
createPong2ClickHandler,
createPong2ViewedHandler,
fetchImage,
} from "../internal/pong/index.js";

import * as env from "../env.js";

import { getRequestCountry } from "../utils.js";

const { SIGN_SECRET, BSA_ZONE_KEYS } = env;

const coder = new Coder(SIGN_SECRET);
const handleGet = createPong2GetHandler(BSA_ZONE_KEYS, coder, env);
const handleClick = createPong2ClickHandler(coder);
const handleViewed = createPong2ViewedHandler(coder);

export async function proxyBSA(req: Request, res: Response) {
const countryCode = getRequestCountry(req);

const userAgent = req.headers["user-agent"] ?? "";

const parsedUrl = url.parse(req.url);
const pathname = parsedUrl.pathname ?? "";
const search = parsedUrl.search ?? "";

if (pathname === "/pong/get") {
if (req.method !== "POST") {
return res.sendStatus(405).end();
}

const { body } = req;
const { statusCode: status, payload } = await handleGet(
body,
countryCode,
userAgent
);

return res
.status(status)
.setHeader("cache-control", "no-store")
.setHeader("content-type", "application/json")
.end(JSON.stringify(payload));
} else if (req.path === "/pong/click") {
if (req.method !== "GET") {
return res.sendStatus(405).end();
}
const params = new URLSearchParams(search);
try {
const { status, location } = await handleClick(params);
if (location && (status === 301 || status === 302)) {
return res.redirect(location);
} else {
return res.sendStatus(502).end();
}
} catch (e) {
console.error(e);
}
} else if (pathname === "/pong/viewed") {
if (req.method !== "POST") {
return res.sendStatus(405).end();
}
const params = new URLSearchParams(search);
try {
await handleViewed(params);
return res.sendStatus(201).end();
} catch (e) {
console.error(e);
}
} else if (pathname.startsWith("/pimg/")) {
const src = coder.decodeAndVerify(
decodeURIComponent(pathname.substring("/pimg/".length))
);
if (!src) {
return res.sendStatus(400).end();
}
const { buf, contentType } = await fetchImage(src);
return res
.status(200)
.set({
"cache-control": "max-age=86400",
"content-type": contentType,
})
.end(Buffer.from(buf));
}

return res.status(204).end();
}
16 changes: 16 additions & 0 deletions cloud-function/src/handlers/proxy-pong.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { BSA_ENABLED, BSA_URL_PREFIX } from "../env.js";
import { proxyBSA } from "./proxy-bsa.js";
import { proxyKevel } from "./proxy-kevel.js";

import type { Request, Response } from "express";

export async function proxyPong(req: Request, res: Response) {
if (BSA_ENABLED) {
const referrer = req.get("referrer") || "";
const version = Number(req.query["version"]) || 1;
if (referrer.startsWith(BSA_URL_PREFIX) || version === 2) {
return proxyBSA(req, res);
}
}
return proxyKevel(req, res);
}
Loading

0 comments on commit 4d9413d

Please sign in to comment.