From fb27fac524360a9ee825a03de570baee76ac95c6 Mon Sep 17 00:00:00 2001 From: James Grugett Date: Mon, 19 Sep 2022 14:55:37 -0500 Subject: [PATCH] Revalidate getStaticProps after each bet --- functions/README.md | 3 ++- functions/src/on-create-bet.ts | 11 ++++++++- functions/src/utils.ts | 16 +++++++++++++ web/pages/api/v0/revalidate.ts | 42 ++++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 web/pages/api/v0/revalidate.ts diff --git a/functions/README.md b/functions/README.md index 97a7a33b..02477588 100644 --- a/functions/README.md +++ b/functions/README.md @@ -65,5 +65,6 @@ Adapted from https://firebase.google.com/docs/functions/get-started Secrets are strings that shouldn't be checked into Git (eg API keys, passwords). We store these using [Google Secret Manager](https://console.cloud.google.com/security/secret-manager), which provides them as environment variables to functions that require them. Some useful workflows: -- Set a secret: `$ firebase functions:secrets:set stripe.test_secret="THE-API-KEY"` +- Set a secret: `$ firebase functions:secrets:set STRIPE_APIKEY` + - Then, enter the secret in the prompt. - Read a secret: `$ firebase functions:secrets:access STRIPE_APIKEY` diff --git a/functions/src/on-create-bet.ts b/functions/src/on-create-bet.ts index 54f7a7f4..207a75aa 100644 --- a/functions/src/on-create-bet.ts +++ b/functions/src/on-create-bet.ts @@ -3,7 +3,14 @@ import * as admin from 'firebase-admin' import { keyBy, uniq } from 'lodash' import { Bet, LimitBet } from '../../common/bet' -import { getUser, getValues, isProd, log } from './utils' +import { + getContractPath, + getUser, + getValues, + isProd, + log, + revalidateStaticProps, +} from './utils' import { createBetFillNotification, createBettingStreakBonusNotification, @@ -72,6 +79,8 @@ export const onCreateBet = functions await updateBettingStreak(bettor, bet, contract, eventId) await firestore.collection('users').doc(bettor.id).update({ lastBetTime }) + + await revalidateStaticProps(getContractPath(contract)) }) const updateBettingStreak = async ( diff --git a/functions/src/utils.ts b/functions/src/utils.ts index 23f7257a..aa282ebf 100644 --- a/functions/src/utils.ts +++ b/functions/src/utils.ts @@ -17,6 +17,18 @@ export const logMemory = () => { } } +export const revalidateStaticProps = async ( + // Path after domain: e.g. "/JamesGrugett/will-pete-buttigieg-ever-be-us-pres" + pathToRevalidate: string +) => { + if (isProd()) { + const apiSecret = process.env.API_SECRET as string + const queryStr = `?pathToRevalidate=${pathToRevalidate}&apiSecret=${apiSecret}` + await fetch('https://manifold.markets' + queryStr) + console.log('Revalidated', pathToRevalidate) + } +} + export type UpdateSpec = { doc: admin.firestore.DocumentReference fields: { [k: string]: unknown } @@ -153,3 +165,7 @@ export const chargeUser = ( return updateUserBalance(userId, -charge, isAnte) } + +export const getContractPath = (contract: Contract) => { + return `/${contract.creatorUsername}/${contract.slug}` +} diff --git a/web/pages/api/v0/revalidate.ts b/web/pages/api/v0/revalidate.ts new file mode 100644 index 00000000..9b002e6a --- /dev/null +++ b/web/pages/api/v0/revalidate.ts @@ -0,0 +1,42 @@ +import type { NextApiRequest, NextApiResponse } from 'next' +import { z } from 'zod' +import { ValidationError } from './_types' +import { validate } from './_validate' + +const queryParams = z + .object({ + // This secret is stored in both Firebase and Vercel's environment variables, as API_SECRET. + apiSecret: z.string(), + // Path after domain: e.g. "/JamesGrugett/will-pete-buttigieg-ever-be-us-pres" + pathToRevalidate: z.string(), + }) + .strict() + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + let params: z.infer + try { + params = validate(queryParams, req.query) + } catch (e) { + if (e instanceof ValidationError) { + return res.status(400).json(e) + } + console.error(`Unknown error during validation: ${e}`) + return res.status(500).json({ error: 'Unknown error during validation' }) + } + + const { apiSecret, pathToRevalidate } = params + + if (apiSecret !== process.env.API_SECRET) { + return res.status(401).json({ message: 'Invalid api secret' }) + } + + try { + await res.revalidate(pathToRevalidate) + return res.json({ revalidated: true }) + } catch (err) { + return res.status(500).send('Error revalidating') + } +}