Skip to content

Commit 0682c73

Browse files
committed
feat: added fetch routes
1 parent effbc28 commit 0682c73

File tree

13 files changed

+152
-99
lines changed

13 files changed

+152
-99
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { stringify } from 'superjson';
2+
3+
import { getFeedsCached } from "../../../../../server/pyth/get-feeds";
4+
import { Cluster } from "../../../../../services/pyth";
5+
6+
export const GET = async (_: Request, { params }: { params: Promise<{ symbol: string }> }) => {
7+
const { symbol } = await params;
8+
9+
const feeds = await getFeedsCached(Cluster.Pythnet);
10+
const feed = feeds.find((feed) => feed.symbol === symbol);
11+
return new Response(stringify(feed), {
12+
headers: {
13+
'Content-Type': 'application/json',
14+
}
15+
});
16+
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { NextResponse } from "next/server";
2+
import { stringify } from 'superjson';
3+
import type { z } from 'zod';
4+
5+
import { getFeedsCached } from "../../../../server/pyth/get-feeds";
6+
import { Cluster, priceFeedsSchema } from "../../../../services/pyth";
7+
8+
export const GET = async (request: Request) => {
9+
// get cluster from query params
10+
const { searchParams } = new URL(request.url);
11+
const excludePriceComponents = searchParams.get("excludePriceComponents") === "true";
12+
const cluster = Number.parseInt(searchParams.get("cluster") ?? Cluster.Pythnet.toString()) as Cluster;
13+
// check if cluster is valid
14+
if (cluster && !Object.values(Cluster).includes(cluster)) {
15+
return NextResponse.json({ error: "Invalid cluster" }, { status: 400 });
16+
}
17+
18+
let feeds = await getFeedsCached(cluster) as Omit<z.infer<typeof priceFeedsSchema>[number], 'price'>[];
19+
if(excludePriceComponents) {
20+
feeds = feeds.map((feed) => ({
21+
...feed,
22+
price: undefined,
23+
}));
24+
}
25+
return new Response(stringify(feeds), {
26+
headers: {
27+
'Content-Type': 'application/json',
28+
},
29+
});
30+
};
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { NextResponse } from "next/server";
2+
import { stringify } from 'superjson';
3+
4+
import { getPublishersForCluster } from "../../../../server/pyth/get-publishers-for-cluster";
5+
import { Cluster } from "../../../../services/pyth";
6+
7+
export const GET = async (request: Request) => {
8+
// get cluster from query params
9+
const { searchParams } = new URL(request.url);
10+
const cluster = Number.parseInt(searchParams.get("cluster") ?? Cluster.Pythnet.toString()) as Cluster;
11+
// check if cluster is valid
12+
if (cluster && !Object.values(Cluster).includes(cluster)) {
13+
return NextResponse.json({ error: "Invalid cluster" }, { status: 400 });
14+
}
15+
const publishers = await getPublishersForCluster(cluster);
16+
return new Response(stringify(publishers), {
17+
headers: {
18+
'Content-Type': 'application/json',
19+
},
20+
});
21+
};

apps/insights/src/components/Overview/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import { FormattedDate } from "../FormattedDate";
2424
import { FormattedNumber } from "../FormattedNumber";
2525

2626
export const Overview = async () => {
27+
// eslint-disable-next-line no-console
28+
console.log('overview');
2729
const priceFeeds = await getFeeds(Cluster.Pythnet);
2830
const today = new Date();
2931
const feedCounts = [
Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,37 @@
1-
import { notFound } from "next/navigation";
1+
import { parse } from "superjson";
2+
import { z } from "zod";
23

3-
import { getFeeds } from "../../server/pyth/get-feeds";
4-
import { Cluster } from "../../services/pyth";
4+
import { PUBLIC_URL, VERCEL_AUTOMATION_BYPASS_SECRET } from '../../config/server';
5+
import { Cluster, priceFeedsSchema } from "../../services/pyth";
56

67
export const getFeed = async (params: Promise<{ slug: string }>) => {
7-
const [{ slug }, feeds] = await Promise.all([params, getFeeds(Cluster.Pythnet)]);
8+
const data = await fetch(`${PUBLIC_URL}/api/pyth/get-feeds?cluster=${Cluster.Pythnet.toString()}&excludePriceComponents=true`, {
9+
next: {
10+
revalidate: 1000 * 60 * 60 * 24,
11+
},
12+
headers: {
13+
'x-vercel-protection-bypass': VERCEL_AUTOMATION_BYPASS_SECRET,
14+
},
15+
});
16+
const dataJson = await data.text();
17+
const feeds: z.infer<typeof priceFeedsSchema> = parse(dataJson);
18+
19+
const { slug } = await params;
820
const symbol = decodeURIComponent(slug);
21+
const feed = await fetch(`${PUBLIC_URL}/api/pyth/get-feeds/${encodeURIComponent(symbol)}`, {
22+
next: {
23+
revalidate: 1000 * 60 * 60 * 24,
24+
},
25+
headers: {
26+
'x-vercel-protection-bypass': VERCEL_AUTOMATION_BYPASS_SECRET,
27+
},
28+
});
29+
const feedJson = await feed.text();
30+
const feedData: z.infer<typeof priceFeedsSchema>[0] = parse(feedJson);
31+
932
return {
1033
feeds,
11-
feed: feeds.find((item) => item.symbol === symbol) ?? notFound(),
34+
feed: feedData,
1235
symbol,
1336
} as const;
1437
};

apps/insights/src/components/PriceFeeds/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,8 @@ const FeaturedFeedsCard = <T extends ElementType>({
290290
);
291291

292292
const getPriceFeeds = async () => {
293+
// eslint-disable-next-line no-console
294+
console.log('getPriceFeeds');
293295
const priceFeeds = await getFeeds(Cluster.Pythnet);
294296
const activeFeeds = priceFeeds.filter((feed) => isActive(feed));
295297
const comingSoon = priceFeeds.filter((feed) => !isActive(feed));

apps/insights/src/components/Root/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ const getPublishersForSearchDialog = async (cluster: Cluster) => {
7575
};
7676

7777
const getFeedsForSearchDialog = async (cluster: Cluster) => {
78+
// eslint-disable-next-line no-console
79+
console.log('getFeedsForSearchDialog');
7880
const feeds = await getFeeds(cluster);
7981

8082
return feeds.map((feed) => ({

apps/insights/src/config/server.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const getEnvOrDefault = (key: string, defaultValue: string) =>
3131
* Indicates that this server is the live customer-facing production server.
3232
*/
3333
export const IS_PRODUCTION_SERVER = process.env.VERCEL_ENV === "production";
34+
export const IS_PREVIEW_SERVER = process.env.VERCEL_ENV === "preview";
3435

3536
const defaultInProduction = IS_PRODUCTION_SERVER
3637
? getEnvOrDefault
@@ -59,19 +60,35 @@ export const ENABLE_ACCESSIBILITY_REPORTING =
5960
!IS_PRODUCTION_SERVER && !process.env.DISABLE_ACCESSIBILITY_REPORTING;
6061

6162

62-
export async function getRedis(): Promise<Redis> {
63+
let redisClient: Redis | undefined;
64+
65+
export function getRedis(): Redis {
6366
const host = process.env.REDIS_HOST;
6467
const port = process.env.REDIS_PORT;
6568
const password = process.env.REDIS_PASSWORD;
6669
if (!host || !port) {
6770
throw new Error('REDIS_HOST, and REDIS_PORT must be set');
6871
}
69-
const client = new Redis({
72+
redisClient ??= new Redis({
7073
username: 'default',
7174
password: password ?? '',
7275
host,
7376
port: Number.parseInt(port),
74-
});
75-
await client.set('test2', Math.random().toString());
76-
return client;
77-
}
77+
});
78+
return redisClient;
79+
}
80+
81+
export const PUBLIC_URL = (() => {
82+
if (IS_PRODUCTION_SERVER) {
83+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, turbo/no-undeclared-env-vars
84+
return `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL!}`;
85+
} else if (IS_PREVIEW_SERVER) {
86+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, turbo/no-undeclared-env-vars
87+
return `https://${process.env.VERCEL_URL!}`;
88+
} else {
89+
return `http://localhost:3003`;
90+
}
91+
})();
92+
93+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, turbo/no-undeclared-env-vars
94+
export const VERCEL_AUTOMATION_BYPASS_SECRET = process.env.VERCEL_AUTOMATION_BYPASS_SECRET!;
Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,22 @@
1-
21
import { getPublisherAverageScoreHistory, getPublisherRankingHistory, getPublishers, getRankingsByPublisher, getRankingsBySymbol } from "../services/clickhouse";
32
import { Cluster } from "../services/pyth";
4-
import { createChunkedCacheFetcher, fetchAllChunks } from '../utils/cache';
5-
6-
7-
const _getRankingsBySymbol = createChunkedCacheFetcher(async (symbol: string) => {
8-
return getRankingsBySymbol(symbol);
9-
}, 'getRankingsBySymbol');
103

114
export const getRankingsBySymbolCached = async (symbol: string) => {
12-
return fetchAllChunks<ReturnType<typeof getRankingsBySymbol>, [string]>(_getRankingsBySymbol, symbol);
5+
return getRankingsBySymbol(symbol);
136
};
147

15-
const _getRankingsByPublisher = createChunkedCacheFetcher(async (publisherKey: string) => {
16-
return getRankingsByPublisher(publisherKey);
17-
}, 'getRankingsByPublisher');
18-
198
export const getRankingsByPublisherCached = async (publisherKey: string) => {
20-
return fetchAllChunks<ReturnType<typeof getRankingsByPublisher>, [string]>(_getRankingsByPublisher, publisherKey);
9+
return getRankingsByPublisher(publisherKey);
2110
};
2211

23-
const _getPublisherAverageScoreHistory = createChunkedCacheFetcher(async (cluster: Cluster, key: string) => {
24-
return getPublisherAverageScoreHistory(cluster, key);
25-
}, 'getPublisherAverageScoreHistory');
26-
2712
export const getPublisherAverageScoreHistoryCached = async (cluster: Cluster, key: string) => {
28-
return fetchAllChunks<ReturnType<typeof getPublisherAverageScoreHistory>, [Cluster, string]>(_getPublisherAverageScoreHistory, cluster, key);
13+
return getPublisherAverageScoreHistory(cluster, key);
2914
};
3015

31-
const _getPublisherRankingHistory = createChunkedCacheFetcher(async (cluster: Cluster, key: string) => {
32-
return getPublisherRankingHistory(cluster, key);
33-
}, 'getPublisherRankingHistory');
34-
3516
export const getPublisherRankingHistoryCached = async (cluster: Cluster, key: string) => {
36-
return fetchAllChunks<ReturnType<typeof getPublisherRankingHistory>, [Cluster, string]>(_getPublisherRankingHistory, cluster, key);
17+
return getPublisherRankingHistory(cluster, key);
3718
};
3819

39-
const _getPublishers = createChunkedCacheFetcher(async (cluster: Cluster) => {
40-
return getPublishers(cluster);
41-
}, 'getPublishers');
42-
4320
export const getPublishersCached = async (cluster: Cluster) => {
44-
return fetchAllChunks<ReturnType<typeof getPublishers>, [Cluster]>(_getPublishers, cluster);
21+
return getPublishers(cluster);
4522
}

apps/insights/src/server/pyth.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export async function getFeedsForPublisherCached(
1515
cluster: Cluster,
1616
publisher: string
1717
) {
18+
// eslint-disable-next-line no-console
19+
console.log('getFeedsForPublisherCached');
1820
const data = await getFeeds(cluster);
1921
return priceFeedsSchema.parse(
2022
data.filter(({ price }) =>

0 commit comments

Comments
 (0)