Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/staking/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"dnum": "^2.13.1",
"next": "^14.2.5",
"pino": "^9.3.2",
"proxycheck-ts": "^0.0.11",
"react": "^18.3.1",
"react-aria": "^3.34.3",
"react-aria-components": "^1.3.3",
Expand Down
1 change: 0 additions & 1 deletion apps/staking/src/app/blocked/page.tsx

This file was deleted.

1 change: 1 addition & 0 deletions apps/staking/src/app/region-blocked/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { RegionBlocked as default } from "../../components/Blocked";
1 change: 1 addition & 0 deletions apps/staking/src/app/vpn-blocked/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { VpnBlocked as default } from "../../components/Blocked";
18 changes: 14 additions & 4 deletions apps/staking/src/components/Blocked/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import { LinkButton } from "../Button";

export const Blocked = () => (
export const RegionBlocked = () => (
<Blocked reason="This program is currently unavailable to users in your region" />
);

export const VpnBlocked = () => (
<Blocked reason="You cannot access this app via a VPN. Please disable your VPN and try aagin." />
);

type Props = {
reason: string;
};

const Blocked = ({ reason }: Props) => (
<main className="grid size-full place-content-center py-20 text-center">
<h1 className="mb-8 text-4xl font-semibold text-pythpurple-400">
{"We're sorry"}
</h1>
<p className="mb-20 text-lg">
This program is currently unavailable to users in your region
</p>
<p className="mb-20 text-lg">{reason}</p>
<LinkButton
className="place-self-center px-24 py-3"
href="https://www.pyth.network"
Expand Down
10 changes: 8 additions & 2 deletions apps/staking/src/components/WalletButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ import {
SubmenuTrigger,
} from "react-aria-components";

import { BLOCKED_SEGMENT } from "../../config/isomorphic";
import {
REGION_BLOCKED_SEGMENT,
VPN_BLOCKED_SEGMENT,
} from "../../config/isomorphic";
import {
StateType as ApiStateType,
type States,
Expand All @@ -49,8 +52,11 @@ type Props = Omit<ComponentProps<typeof Button>, "onClick" | "children">;

export const WalletButton = (props: Props) => {
const segment = useSelectedLayoutSegment();
const isBlocked =
segment === REGION_BLOCKED_SEGMENT || segment === VPN_BLOCKED_SEGMENT;

// eslint-disable-next-line unicorn/no-null
return segment === BLOCKED_SEGMENT ? null : <WalletButtonImpl {...props} />;
return isBlocked ? null : <WalletButtonImpl {...props} />;
};

const WalletButtonImpl = (props: Props) => {
Expand Down
10 changes: 9 additions & 1 deletion apps/staking/src/config/isomorphic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,12 @@ export const IS_PRODUCTION_BUILD = process.env.NODE_ENV === "production";
*
* Don't change unless you also change the relevant app route path to match.
*/
export const BLOCKED_SEGMENT = "blocked";
export const REGION_BLOCKED_SEGMENT = "region-blocked";

/**
* Similar to `REGION_BLOCKED_SEGMENT`; this is where vpn-blocked traffic will
* be rewritten to.
*
* Don't change unless you also change the relevant app route path to match.
*/
export const VPN_BLOCKED_SEGMENT = "vpn-blocked";
1 change: 1 addition & 0 deletions apps/staking/src/config/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const BLOCKED_REGIONS =
: process.env.BLOCKED_REGIONS.split(",").map((region) =>
region.toLowerCase().trim(),
);
export const PROXYCHECK_API_KEY = demandInProduction("PROXYCHECK_API_KEY");

class MissingEnvironmentError extends Error {
constructor(name: string) {
Expand Down
43 changes: 34 additions & 9 deletions apps/staking/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,47 @@
import { type NextRequest, NextResponse } from "next/server";
import ProxyCheck from "proxycheck-ts";

import { BLOCKED_SEGMENT } from "./config/isomorphic";
import { BLOCKED_REGIONS } from "./config/server";
import {
REGION_BLOCKED_SEGMENT,
VPN_BLOCKED_SEGMENT,
} from "./config/isomorphic";
import { BLOCKED_REGIONS, PROXYCHECK_API_KEY } from "./config/server";

export const middleware = (request: NextRequest) => {
if (blockRequest(request)) {
return NextResponse.rewrite(new URL(`/${BLOCKED_SEGMENT}`, request.url));
} else if (request.nextUrl.pathname.startsWith("/blocked")) {
return NextResponse.rewrite(new URL("/not-found", request.url));
const proxyCheckClient = PROXYCHECK_API_KEY
? new ProxyCheck({ api_key: PROXYCHECK_API_KEY })
: undefined;

export const middleware = async (request: NextRequest) => {
if (isRegionBlocked(request)) {
return NextResponse.rewrite(
new URL(`/${REGION_BLOCKED_SEGMENT}`, request.url),
);
} else if (await isProxyBlocked(request)) {
return NextResponse.rewrite(
new URL(`/${VPN_BLOCKED_SEGMENT}`, request.url),
);
} else {
return;
const { pathname } = request.nextUrl;
return pathname.startsWith(`/${REGION_BLOCKED_SEGMENT}`) ||
pathname.startsWith(`/${VPN_BLOCKED_SEGMENT}`)
? NextResponse.rewrite(new URL("/not-found", request.url))
: undefined;
}
};

const blockRequest = (request: NextRequest) =>
const isRegionBlocked = (request: NextRequest) =>
request.geo?.country !== undefined &&
BLOCKED_REGIONS.includes(request.geo.country.toLowerCase());

const isProxyBlocked = async ({ ip }: NextRequest) => {
if (proxyCheckClient === undefined || ip === undefined) {
return false;
} else {
const result = await proxyCheckClient.checkIP(ip, { vpn: 2 });
return result[ip]?.proxy === "yes";
}
};

export const config = {
matcher: [
"/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
Expand Down
Loading
Loading