diff --git a/apps/cow-fi/const/meta.ts b/apps/cow-fi/const/meta.ts
index b30ac93756..dc2f8b528e 100644
--- a/apps/cow-fi/const/meta.ts
+++ b/apps/cow-fi/const/meta.ts
@@ -3,7 +3,7 @@ import { TokenInfo } from 'types'
const API_BASE_URL = 'https://api.cow.fi'
export const IMAGE_PATH = 'images/'
-export const DATA_CACHE_TIME_SECONDS = 5 * 60 // Cache 5min
+export const DATA_CACHE_TIME_SECONDS = 60 * 60 // Cache 1 hour
export const CONFIG = {
title: 'CoW DAO',
diff --git a/apps/cow-fi/next.config.js b/apps/cow-fi/next.config.js
index eea085aaa7..914f6503b2 100644
--- a/apps/cow-fi/next.config.js
+++ b/apps/cow-fi/next.config.js
@@ -1,6 +1,7 @@
const { composePlugins, withNx } = require('@nx/next')
const nextConfig = {
+ reactStrictMode: true,
nx: {
svgr: false,
},
@@ -91,6 +92,20 @@ const nextConfig = {
images: {
domains: ['celebrated-gift-f83e5c9419.media.strapiapp.com'],
},
+ async headers() {
+ return [
+ // Cache all pages for 60 seconds
+ {
+ source: '/:path*',
+ headers: [
+ {
+ key: 'Cache-Control',
+ value: 'public, s-maxage=60, stale-while-revalidate=600',
+ },
+ ],
+ },
+ ]
+ },
}
const plugins = [withNx]
diff --git a/apps/cow-fi/pages/_app.tsx b/apps/cow-fi/pages/_app.tsx
index 49bbe0b0dd..9aedb90832 100644
--- a/apps/cow-fi/pages/_app.tsx
+++ b/apps/cow-fi/pages/_app.tsx
@@ -10,6 +10,7 @@ import { WithLDProvider } from '@/components/WithLDProvider'
import { ThemeProvider } from '../theme'
import { CowAnalyticsProvider } from '@cowprotocol/analytics'
import { cowAnalytics } from 'modules/analytics'
+import CacheProvider from 'react-inlinesvg/provider'
export default function App(props: AppProps) {
const { Component, pageProps } = props
@@ -54,15 +55,17 @@ export default function App(props: AppProps) {
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
>
)
}
diff --git a/apps/cow-fi/services/ashByHq/index.ts b/apps/cow-fi/services/ashByHq/index.ts
index 140aae86fa..d5136e698a 100644
--- a/apps/cow-fi/services/ashByHq/index.ts
+++ b/apps/cow-fi/services/ashByHq/index.ts
@@ -1,4 +1,4 @@
-import { CONFIG } from '@/const/meta'
+import { CONFIG, DATA_CACHE_TIME_SECONDS } from '@/const/meta'
interface AshbyResponse {
data: {
@@ -25,6 +25,7 @@ export async function getJobs() {
try {
console.log('Fetching data from Ashby HQ API...')
const response = await fetch(ashbyHqApi, {
+ next: { revalidate: DATA_CACHE_TIME_SECONDS },
method: 'POST',
headers: {
'Content-Type': 'application/json',
diff --git a/apps/cow-fi/services/cms/index.ts b/apps/cow-fi/services/cms/index.ts
index d1a1bb6c24..ef79552118 100644
--- a/apps/cow-fi/services/cms/index.ts
+++ b/apps/cow-fi/services/cms/index.ts
@@ -1,9 +1,10 @@
-import { CmsClient, components } from '@cowprotocol/cms'
+import { components } from '@cowprotocol/cms'
import { PaginationParam } from 'types'
import qs from 'qs'
import { toQueryParams } from 'util/queryParams'
import { getCmsClient } from '@cowprotocol/core'
+import { DATA_CACHE_TIME_SECONDS } from '@/const/meta'
const PAGE_SIZE = 50
@@ -22,56 +23,17 @@ export type ArticleListResponse = {
}
}
-export type SharedMediaComponent = Schemas['SharedMediaComponent']
-export type SharedQuoteComponent = Schemas['SharedQuoteComponent']
export type SharedRichTextComponent = Schemas['SharedRichTextComponent']
-export type SharedSliderComponent = Schemas['SharedSliderComponent']
-export type SharedVideoEmbedComponent = Schemas['SharedVideoEmbedComponent']
export type Category = Schemas['CategoryListResponseDataItem']
-export type ArticleCover = Schemas['Article']['cover']
-export type ArticleBlocks = Schemas['Article']['blocks']
-
-export type ArticleBlock =
- | SharedMediaComponent
- | SharedQuoteComponent
- | SharedRichTextComponent
- | SharedSliderComponent
- | SharedVideoEmbedComponent
-
-export function isSharedMediaComponent(component: ArticleBlock): component is SharedMediaComponent {
- return component.__component === 'SharedMediaComponent'
-}
-
-export function isSharedQuoteComponent(component: ArticleBlock): component is SharedQuoteComponent {
- return component.__component === 'SharedQuoteComponent'
-}
-
-export function isSharedRichTextComponent(component: ArticleBlock): component is SharedRichTextComponent {
- return component.__component === 'shared.rich-text'
-}
-
-export function isSharedSliderComponent(component: ArticleBlock): component is SharedMediaComponent {
- return component.__component === 'SharedSliderComponent'
-}
-
-export function isSharedVideoEmbedComponent(component: ArticleBlock): component is SharedVideoEmbedComponent {
- return component.__component === 'SharedVideoEmbedComponent'
-}
/**
* Open API Fetch client. See docs for usage https://openapi-ts.pages.dev/openapi-fetch/
*/
export const client = getCmsClient()
-/**
- * Returns the article slugs for the given page.
- *
- * @param params pagination params
- * @returns Slugs
- */
-async function getArticlesSlugs(params: PaginationParam = {}): Promise {
- const articlesResponse = await getArticles(params)
- return articlesResponse.data.map((article: Article) => article.attributes!.slug!)
+const clientAddons = {
+ // https://github.com/openapi-ts/openapi-typescript/issues/1569#issuecomment-1982247959
+ fetch: (request: unknown) => fetch(request as Request, { next: { revalidate: DATA_CACHE_TIME_SECONDS } }),
}
/**
@@ -92,6 +54,7 @@ export async function getAllArticleSlugs(): Promise {
},
},
querySerializer,
+ ...clientAddons,
})
if (error) {
@@ -119,6 +82,7 @@ export async function getCategories(): Promise {
},
sort: 'name:asc',
},
+ ...clientAddons,
})
if (error) {
@@ -174,6 +138,7 @@ export async function getArticles({
},
},
querySerializer,
+ ...clientAddons,
})
if (error) {
@@ -211,6 +176,7 @@ export async function getArticleBySlug(slug: string): Promise {
},
},
querySerializer,
+ ...clientAddons,
})
if (error) {
@@ -303,6 +269,7 @@ async function getBySlugAux(slug: string, endpoint: '/categories' | '/articles')
params: {
query,
},
+ ...clientAddons,
})
if (error) {
diff --git a/apps/cow-fi/services/cow/index.tsx b/apps/cow-fi/services/cow/index.tsx
index 3dd5d53c85..d4585c8756 100644
--- a/apps/cow-fi/services/cow/index.tsx
+++ b/apps/cow-fi/services/cow/index.tsx
@@ -1,18 +1,23 @@
+import { DATA_CACHE_TIME_SECONDS } from '@/const/meta'
+
const CONFIG_PATH = 'https://raw.githubusercontent.com/cowprotocol/cow-fi/configuration/config/'
export interface CowStats {
- totalTrades: number, // https://dune.com/queries/1034337
- surplus: { // https://dune.com/queries/270604
- reasonable: number,
+ totalTrades: number // https://dune.com/queries/1034337
+ surplus: {
+ // https://dune.com/queries/270604
+ reasonable: number
unusual: number
- },
+ }
lastModified: Date
}
type CowStatsConfig = Omit & { lastModified: string }
async function getFromConfig(configFilePath: string): Promise {
- const response = await fetch(CONFIG_PATH + `${configFilePath}`)
+ const response = await fetch(CONFIG_PATH + `${configFilePath}`, {
+ next: { revalidate: DATA_CACHE_TIME_SECONDS },
+ })
return await response.json()
}
@@ -20,6 +25,6 @@ export async function getCowStats(): Promise {
const statsConfig = await getFromConfig('stats.json')
return {
...statsConfig,
- lastModified: new Date(statsConfig.lastModified)
+ lastModified: new Date(statsConfig.lastModified),
}
}
diff --git a/apps/cow-fi/services/dune/index.tsx b/apps/cow-fi/services/dune/index.tsx
index b4447831e6..ec292c090c 100644
--- a/apps/cow-fi/services/dune/index.tsx
+++ b/apps/cow-fi/services/dune/index.tsx
@@ -1,3 +1,4 @@
+import { DATA_CACHE_TIME_SECONDS } from '@/const/meta'
import { strict as assert } from 'node:assert'
const DUNE_API_KEY = process.env.DUNE_API_KEY!
@@ -21,6 +22,7 @@ interface GetFromDuneResult {
export async function getFromDune(queryId: number): Promise> {
const response = await fetch(`https://api.dune.com/api/v0/query/${queryId}/results`, {
+ next: { revalidate: DATA_CACHE_TIME_SECONDS },
headers: {
accept: 'application/json',
'X-DUNE-API-KEY': DUNE_API_KEY,
@@ -47,7 +49,7 @@ export async function _getTotalCount(queryId: number): Promise {
// Expect one row
assert(
queryResut.rows.length === 1,
- `Total Count Dune query (${queryId}) must return just one row. Returned ${queryResut.rows.length}`
+ `Total Count Dune query (${queryId}) must return just one row. Returned ${queryResut.rows.length}`,
)
return {
@@ -66,7 +68,7 @@ export const getTotalTrades = () => _getTotalCount(TOTAL_TRADES_COUNT_QUERY_ID)
*/
export const getTotalSurplus = async (): Promise => {
const queryResut = await getFromDune<{ surplus_type: string; total_surplus_usd: number }>(
- TOTAL_SURPLUS_COUNT_QUERY_ID
+ TOTAL_SURPLUS_COUNT_QUERY_ID,
)
const totalCount = queryResut.rows.reduce((totalSurplus, surplus) => {
diff --git a/apps/cow-fi/services/tokens/index.ts b/apps/cow-fi/services/tokens/index.ts
index 4a242fa992..75f0719e2a 100644
--- a/apps/cow-fi/services/tokens/index.ts
+++ b/apps/cow-fi/services/tokens/index.ts
@@ -2,6 +2,7 @@ import fs from 'fs'
import path from 'path'
import { PlatformData, Platforms, TokenDetails, TokenInfo } from 'types'
import { backOff } from 'exponential-backoff'
+import { DATA_CACHE_TIME_SECONDS } from '@/const/meta'
const NETWORKS = ['ethereum', 'xdai']
const COW_TOKEN_ID = 'cow-protocol'
@@ -72,8 +73,9 @@ function _getDescriptionFilePaths(): string[] {
async function fetchWithBackoff(url: string) {
return backOff(
() => {
- console.log(`Fetching ${url}`)
- return fetch(url).then((res) => {
+ return fetch(url, {
+ next: { revalidate: DATA_CACHE_TIME_SECONDS },
+ }).then((res) => {
if (!res.ok) {
throw new Error(`Error fetching list ${url}: Error ${res.status}, ${res.statusText}`)
}
diff --git a/package.json b/package.json
index 8b05424032..3f16f55971 100644
--- a/package.json
+++ b/package.json
@@ -201,7 +201,7 @@
"react-ga4": "^1.4.1",
"react-helmet": "^6.1.0",
"react-icons": "^5.2.1",
- "react-inlinesvg": "^3.0.1",
+ "react-inlinesvg": "^4.1.5",
"react-intersection-observer": "^9.10.1",
"react-is": "19.0.0-rc-66855b96-20241106",
"react-markdown": "^9.0.0",
diff --git a/yarn.lock b/yarn.lock
index 006525c475..87a89909cc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -16514,11 +16514,6 @@ executable@^4.1.0, executable@^4.1.1:
dependencies:
pify "^2.2.0"
-exenv@^1.2.2:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
- integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==
-
exit@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
@@ -26133,10 +26128,10 @@ react-focus-lock@2.5.2:
use-callback-ref "^1.2.5"
use-sidecar "^1.0.5"
-react-from-dom@^0.6.2:
- version "0.6.2"
- resolved "https://registry.yarnpkg.com/react-from-dom/-/react-from-dom-0.6.2.tgz#9da903a508c91c013b55afcd59348b8b0a39bdb4"
- integrity sha512-qvWWTL/4xw4k/Dywd41RBpLQUSq97csuv15qrxN+izNeLYlD9wn5W8LspbfYe5CWbaSdkZ72BsaYBPQf2x4VbQ==
+react-from-dom@^0.7.3:
+ version "0.7.3"
+ resolved "https://registry.yarnpkg.com/react-from-dom/-/react-from-dom-0.7.3.tgz#60e75fde2369ceb0a8f87d88f9cfbeb67b730e43"
+ integrity sha512-9IK6R7+eD1wOAMC2ZCrENev0eK1625cb7vX+cnnOR9LBRNbjKiaJk4ij2zQbcefEXTWjXFhA7CTO1cd8wMONnw==
react-ga4@^1.4.1:
version "1.4.1"
@@ -26158,13 +26153,12 @@ react-icons@^5.2.1:
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-5.2.1.tgz#28c2040917b2a2eda639b0f797bff1888e018e4a"
integrity sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==
-react-inlinesvg@^3.0.1:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/react-inlinesvg/-/react-inlinesvg-3.0.2.tgz#5c59799966ae7926057091b2ac230ddcee01bea0"
- integrity sha512-BEzkpMGQwEY68fgaouY7ZWvAUPb8jbj7dE9iDbWZxstDhMuz9qfpxNgvGSENKcDMdpq/XHduSk/LAmNKin4nKw==
+react-inlinesvg@^4.1.5:
+ version "4.1.5"
+ resolved "https://registry.yarnpkg.com/react-inlinesvg/-/react-inlinesvg-4.1.5.tgz#5d6b4f9008d442e4f184ec25e135360d993dc47e"
+ integrity sha512-DcCnmHhpKAUNp6iLPEEB2HJP3simDlyiy8JPZ1DwGCynrQQGQD04GJTFtai8JK8vRhCmoiBV6hSgj31D42Z3Lg==
dependencies:
- exenv "^1.2.2"
- react-from-dom "^0.6.2"
+ react-from-dom "^0.7.3"
react-inspector@^5.1.0:
version "5.1.1"