Skip to content

Commit 39c94cc

Browse files
authored
feat(cow-fi): cache data and pages (#5169)
1 parent 8f1b3f9 commit 39c94cc

File tree

10 files changed

+69
-80
lines changed

10 files changed

+69
-80
lines changed

apps/cow-fi/const/meta.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { TokenInfo } from 'types'
33
const API_BASE_URL = 'https://api.cow.fi'
44
export const IMAGE_PATH = 'images/'
55

6-
export const DATA_CACHE_TIME_SECONDS = 5 * 60 // Cache 5min
6+
export const DATA_CACHE_TIME_SECONDS = 60 * 60 // Cache 1 hour
77

88
export const CONFIG = {
99
title: 'CoW DAO',

apps/cow-fi/next.config.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const { composePlugins, withNx } = require('@nx/next')
22

33
const nextConfig = {
4+
reactStrictMode: true,
45
nx: {
56
svgr: false,
67
},
@@ -91,6 +92,20 @@ const nextConfig = {
9192
images: {
9293
domains: ['celebrated-gift-f83e5c9419.media.strapiapp.com'],
9394
},
95+
async headers() {
96+
return [
97+
// Cache all pages for 60 seconds
98+
{
99+
source: '/:path*',
100+
headers: [
101+
{
102+
key: 'Cache-Control',
103+
value: 'public, s-maxage=60, stale-while-revalidate=600',
104+
},
105+
],
106+
},
107+
]
108+
},
94109
}
95110

96111
const plugins = [withNx]

apps/cow-fi/pages/_app.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { WithLDProvider } from '@/components/WithLDProvider'
1010
import { ThemeProvider } from '../theme'
1111
import { CowAnalyticsProvider } from '@cowprotocol/analytics'
1212
import { cowAnalytics } from 'modules/analytics'
13+
import CacheProvider from 'react-inlinesvg/provider'
1314

1415
export default function App(props: AppProps) {
1516
const { Component, pageProps } = props
@@ -54,15 +55,17 @@ export default function App(props: AppProps) {
5455
</Head>
5556

5657
<GlobalStyles />
57-
<ApolloProvider client={apolloClient}>
58-
<WithLDProvider>
59-
<ThemeProvider>
60-
<CowAnalyticsProvider cowAnalytics={cowAnalytics}>
61-
<Component {...pageProps} />
62-
</CowAnalyticsProvider>
63-
</ThemeProvider>
64-
</WithLDProvider>
65-
</ApolloProvider>
58+
<CacheProvider>
59+
<ApolloProvider client={apolloClient}>
60+
<WithLDProvider>
61+
<ThemeProvider>
62+
<CowAnalyticsProvider cowAnalytics={cowAnalytics}>
63+
<Component {...pageProps} />
64+
</CowAnalyticsProvider>
65+
</ThemeProvider>
66+
</WithLDProvider>
67+
</ApolloProvider>
68+
</CacheProvider>
6669
</>
6770
)
6871
}

apps/cow-fi/services/ashByHq/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CONFIG } from '@/const/meta'
1+
import { CONFIG, DATA_CACHE_TIME_SECONDS } from '@/const/meta'
22

33
interface AshbyResponse {
44
data: {
@@ -25,6 +25,7 @@ export async function getJobs() {
2525
try {
2626
console.log('Fetching data from Ashby HQ API...')
2727
const response = await fetch(ashbyHqApi, {
28+
next: { revalidate: DATA_CACHE_TIME_SECONDS },
2829
method: 'POST',
2930
headers: {
3031
'Content-Type': 'application/json',

apps/cow-fi/services/cms/index.ts

Lines changed: 10 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { CmsClient, components } from '@cowprotocol/cms'
1+
import { components } from '@cowprotocol/cms'
22
import { PaginationParam } from 'types'
33
import qs from 'qs'
44

55
import { toQueryParams } from 'util/queryParams'
66
import { getCmsClient } from '@cowprotocol/core'
7+
import { DATA_CACHE_TIME_SECONDS } from '@/const/meta'
78

89
const PAGE_SIZE = 50
910

@@ -22,56 +23,17 @@ export type ArticleListResponse = {
2223
}
2324
}
2425

25-
export type SharedMediaComponent = Schemas['SharedMediaComponent']
26-
export type SharedQuoteComponent = Schemas['SharedQuoteComponent']
2726
export type SharedRichTextComponent = Schemas['SharedRichTextComponent']
28-
export type SharedSliderComponent = Schemas['SharedSliderComponent']
29-
export type SharedVideoEmbedComponent = Schemas['SharedVideoEmbedComponent']
3027
export type Category = Schemas['CategoryListResponseDataItem']
31-
export type ArticleCover = Schemas['Article']['cover']
32-
export type ArticleBlocks = Schemas['Article']['blocks']
33-
34-
export type ArticleBlock =
35-
| SharedMediaComponent
36-
| SharedQuoteComponent
37-
| SharedRichTextComponent
38-
| SharedSliderComponent
39-
| SharedVideoEmbedComponent
40-
41-
export function isSharedMediaComponent(component: ArticleBlock): component is SharedMediaComponent {
42-
return component.__component === 'SharedMediaComponent'
43-
}
44-
45-
export function isSharedQuoteComponent(component: ArticleBlock): component is SharedQuoteComponent {
46-
return component.__component === 'SharedQuoteComponent'
47-
}
48-
49-
export function isSharedRichTextComponent(component: ArticleBlock): component is SharedRichTextComponent {
50-
return component.__component === 'shared.rich-text'
51-
}
52-
53-
export function isSharedSliderComponent(component: ArticleBlock): component is SharedMediaComponent {
54-
return component.__component === 'SharedSliderComponent'
55-
}
56-
57-
export function isSharedVideoEmbedComponent(component: ArticleBlock): component is SharedVideoEmbedComponent {
58-
return component.__component === 'SharedVideoEmbedComponent'
59-
}
6028

6129
/**
6230
* Open API Fetch client. See docs for usage https://openapi-ts.pages.dev/openapi-fetch/
6331
*/
6432
export const client = getCmsClient()
6533

66-
/**
67-
* Returns the article slugs for the given page.
68-
*
69-
* @param params pagination params
70-
* @returns Slugs
71-
*/
72-
async function getArticlesSlugs(params: PaginationParam = {}): Promise<string[]> {
73-
const articlesResponse = await getArticles(params)
74-
return articlesResponse.data.map((article: Article) => article.attributes!.slug!)
34+
const clientAddons = {
35+
// https://github.com/openapi-ts/openapi-typescript/issues/1569#issuecomment-1982247959
36+
fetch: (request: unknown) => fetch(request as Request, { next: { revalidate: DATA_CACHE_TIME_SECONDS } }),
7537
}
7638

7739
/**
@@ -92,6 +54,7 @@ export async function getAllArticleSlugs(): Promise<string[]> {
9254
},
9355
},
9456
querySerializer,
57+
...clientAddons,
9558
})
9659

9760
if (error) {
@@ -119,6 +82,7 @@ export async function getCategories(): Promise<Category[]> {
11982
},
12083
sort: 'name:asc',
12184
},
85+
...clientAddons,
12286
})
12387

12488
if (error) {
@@ -174,6 +138,7 @@ export async function getArticles({
174138
},
175139
},
176140
querySerializer,
141+
...clientAddons,
177142
})
178143

179144
if (error) {
@@ -211,6 +176,7 @@ export async function getArticleBySlug(slug: string): Promise<Article | null> {
211176
},
212177
},
213178
querySerializer,
179+
...clientAddons,
214180
})
215181

216182
if (error) {
@@ -303,6 +269,7 @@ async function getBySlugAux(slug: string, endpoint: '/categories' | '/articles')
303269
params: {
304270
query,
305271
},
272+
...clientAddons,
306273
})
307274

308275
if (error) {

apps/cow-fi/services/cow/index.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,30 @@
1+
import { DATA_CACHE_TIME_SECONDS } from '@/const/meta'
2+
13
const CONFIG_PATH = 'https://raw.githubusercontent.com/cowprotocol/cow-fi/configuration/config/'
24

35
export interface CowStats {
4-
totalTrades: number, // https://dune.com/queries/1034337
5-
surplus: { // https://dune.com/queries/270604
6-
reasonable: number,
6+
totalTrades: number // https://dune.com/queries/1034337
7+
surplus: {
8+
// https://dune.com/queries/270604
9+
reasonable: number
710
unusual: number
8-
},
11+
}
912
lastModified: Date
1013
}
1114

1215
type CowStatsConfig = Omit<CowStats, 'lastModified'> & { lastModified: string }
1316

1417
async function getFromConfig<T>(configFilePath: string): Promise<T> {
15-
const response = await fetch(CONFIG_PATH + `${configFilePath}`)
18+
const response = await fetch(CONFIG_PATH + `${configFilePath}`, {
19+
next: { revalidate: DATA_CACHE_TIME_SECONDS },
20+
})
1621
return await response.json()
1722
}
1823

1924
export async function getCowStats(): Promise<CowStats> {
2025
const statsConfig = await getFromConfig<CowStatsConfig>('stats.json')
2126
return {
2227
...statsConfig,
23-
lastModified: new Date(statsConfig.lastModified)
28+
lastModified: new Date(statsConfig.lastModified),
2429
}
2530
}

apps/cow-fi/services/dune/index.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { DATA_CACHE_TIME_SECONDS } from '@/const/meta'
12
import { strict as assert } from 'node:assert'
23

34
const DUNE_API_KEY = process.env.DUNE_API_KEY!
@@ -21,6 +22,7 @@ interface GetFromDuneResult<T> {
2122

2223
export async function getFromDune<T>(queryId: number): Promise<GetFromDuneResult<T>> {
2324
const response = await fetch(`https://api.dune.com/api/v0/query/${queryId}/results`, {
25+
next: { revalidate: DATA_CACHE_TIME_SECONDS },
2426
headers: {
2527
accept: 'application/json',
2628
'X-DUNE-API-KEY': DUNE_API_KEY,
@@ -47,7 +49,7 @@ export async function _getTotalCount(queryId: number): Promise<TotalCount> {
4749
// Expect one row
4850
assert(
4951
queryResut.rows.length === 1,
50-
`Total Count Dune query (${queryId}) must return just one row. Returned ${queryResut.rows.length}`
52+
`Total Count Dune query (${queryId}) must return just one row. Returned ${queryResut.rows.length}`,
5153
)
5254

5355
return {
@@ -66,7 +68,7 @@ export const getTotalTrades = () => _getTotalCount(TOTAL_TRADES_COUNT_QUERY_ID)
6668
*/
6769
export const getTotalSurplus = async (): Promise<TotalCount> => {
6870
const queryResut = await getFromDune<{ surplus_type: string; total_surplus_usd: number }>(
69-
TOTAL_SURPLUS_COUNT_QUERY_ID
71+
TOTAL_SURPLUS_COUNT_QUERY_ID,
7072
)
7173

7274
const totalCount = queryResut.rows.reduce((totalSurplus, surplus) => {

apps/cow-fi/services/tokens/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import fs from 'fs'
22
import path from 'path'
33
import { PlatformData, Platforms, TokenDetails, TokenInfo } from 'types'
44
import { backOff } from 'exponential-backoff'
5+
import { DATA_CACHE_TIME_SECONDS } from '@/const/meta'
56

67
const NETWORKS = ['ethereum', 'xdai']
78
const COW_TOKEN_ID = 'cow-protocol'
@@ -72,8 +73,9 @@ function _getDescriptionFilePaths(): string[] {
7273
async function fetchWithBackoff(url: string) {
7374
return backOff(
7475
() => {
75-
console.log(`Fetching ${url}`)
76-
return fetch(url).then((res) => {
76+
return fetch(url, {
77+
next: { revalidate: DATA_CACHE_TIME_SECONDS },
78+
}).then((res) => {
7779
if (!res.ok) {
7880
throw new Error(`Error fetching list ${url}: Error ${res.status}, ${res.statusText}`)
7981
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@
201201
"react-ga4": "^1.4.1",
202202
"react-helmet": "^6.1.0",
203203
"react-icons": "^5.2.1",
204-
"react-inlinesvg": "^3.0.1",
204+
"react-inlinesvg": "^4.1.5",
205205
"react-intersection-observer": "^9.10.1",
206206
"react-is": "19.0.0-rc-66855b96-20241106",
207207
"react-markdown": "^9.0.0",

yarn.lock

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16514,11 +16514,6 @@ executable@^4.1.0, executable@^4.1.1:
1651416514
dependencies:
1651516515
pify "^2.2.0"
1651616516

16517-
exenv@^1.2.2:
16518-
version "1.2.2"
16519-
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
16520-
integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==
16521-
1652216517
exit@^0.1.2:
1652316518
version "0.1.2"
1652416519
resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
@@ -26133,10 +26128,10 @@ [email protected]:
2613326128
use-callback-ref "^1.2.5"
2613426129
use-sidecar "^1.0.5"
2613526130

26136-
react-from-dom@^0.6.2:
26137-
version "0.6.2"
26138-
resolved "https://registry.yarnpkg.com/react-from-dom/-/react-from-dom-0.6.2.tgz#9da903a508c91c013b55afcd59348b8b0a39bdb4"
26139-
integrity sha512-qvWWTL/4xw4k/Dywd41RBpLQUSq97csuv15qrxN+izNeLYlD9wn5W8LspbfYe5CWbaSdkZ72BsaYBPQf2x4VbQ==
26131+
react-from-dom@^0.7.3:
26132+
version "0.7.3"
26133+
resolved "https://registry.yarnpkg.com/react-from-dom/-/react-from-dom-0.7.3.tgz#60e75fde2369ceb0a8f87d88f9cfbeb67b730e43"
26134+
integrity sha512-9IK6R7+eD1wOAMC2ZCrENev0eK1625cb7vX+cnnOR9LBRNbjKiaJk4ij2zQbcefEXTWjXFhA7CTO1cd8wMONnw==
2614026135

2614126136
react-ga4@^1.4.1:
2614226137
version "1.4.1"
@@ -26158,13 +26153,12 @@ react-icons@^5.2.1:
2615826153
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-5.2.1.tgz#28c2040917b2a2eda639b0f797bff1888e018e4a"
2615926154
integrity sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==
2616026155

26161-
react-inlinesvg@^3.0.1:
26162-
version "3.0.2"
26163-
resolved "https://registry.yarnpkg.com/react-inlinesvg/-/react-inlinesvg-3.0.2.tgz#5c59799966ae7926057091b2ac230ddcee01bea0"
26164-
integrity sha512-BEzkpMGQwEY68fgaouY7ZWvAUPb8jbj7dE9iDbWZxstDhMuz9qfpxNgvGSENKcDMdpq/XHduSk/LAmNKin4nKw==
26156+
react-inlinesvg@^4.1.5:
26157+
version "4.1.5"
26158+
resolved "https://registry.yarnpkg.com/react-inlinesvg/-/react-inlinesvg-4.1.5.tgz#5d6b4f9008d442e4f184ec25e135360d993dc47e"
26159+
integrity sha512-DcCnmHhpKAUNp6iLPEEB2HJP3simDlyiy8JPZ1DwGCynrQQGQD04GJTFtai8JK8vRhCmoiBV6hSgj31D42Z3Lg==
2616526160
dependencies:
26166-
exenv "^1.2.2"
26167-
react-from-dom "^0.6.2"
26161+
react-from-dom "^0.7.3"
2616826162

2616926163
react-inspector@^5.1.0:
2617026164
version "5.1.1"

0 commit comments

Comments
 (0)