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/src/app/blocked/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Blocked as default } from "../../components/Blocked";
19 changes: 19 additions & 0 deletions apps/staking/src/components/Blocked/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { LinkButton } from "../Button";

export const Blocked = () => (
<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>
<LinkButton
className="place-self-center px-24 py-3"
href="https://www.pyth.network"
target="_blank"
>
Read More About Pyth
</LinkButton>
</main>
);
2 changes: 1 addition & 1 deletion apps/staking/src/components/Error/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const Error = ({ error, reset }: Props) => {
}, [error, logger]);

return (
<main className="flex size-full flex-col items-center justify-center gap-2 text-center">
<main className="flex size-full flex-col items-center justify-center gap-2 py-20 text-center">
<div className="mb-10 flex flex-row items-center gap-6">
<ExclamationTriangleIcon className="size-16 text-red-700" />
<h1 className="text-3xl font-light">Uh oh!</h1>
Expand Down
2 changes: 0 additions & 2 deletions apps/staking/src/components/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
"use client";

import clsx from "clsx";
import type { HTMLAttributes } from "react";

Expand Down
2 changes: 1 addition & 1 deletion apps/staking/src/components/NotFound/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { LinkButton } from "../Button";

export const NotFound = () => (
<main className="grid size-full place-content-center text-center">
<main className="grid size-full place-content-center py-20 text-center">
<h1 className="mb-8 text-4xl font-semibold text-pythpurple-400">
Not Found
</h1>
Expand Down
8 changes: 8 additions & 0 deletions apps/staking/src/components/WalletButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useWallet } from "@solana/wallet-adapter-react";
import { useWalletModal } from "@solana/wallet-adapter-react-ui";
import type { PublicKey } from "@solana/web3.js";
import clsx from "clsx";
import { useSelectedLayoutSegment } from "next/navigation";
import {
type ComponentProps,
type ComponentType,
Expand All @@ -33,6 +34,7 @@ import {
SubmenuTrigger,
} from "react-aria-components";

import { BLOCKED_SEGMENT } from "../../config/isomorphic";
import {
StateType as ApiStateType,
type States,
Expand All @@ -47,6 +49,12 @@ import { ModalDialog } from "../ModalDialog";
type Props = Omit<ComponentProps<typeof Button>, "onClick" | "children">;

export const WalletButton = (props: Props) => {
const segment = useSelectedLayoutSegment();
// eslint-disable-next-line unicorn/no-null
return segment === BLOCKED_SEGMENT ? null : <WalletButtonImpl {...props} />;
};

const WalletButtonImpl = (props: Props) => {
const api = useApi();

switch (api.type) {
Expand Down
10 changes: 10 additions & 0 deletions apps/staking/src/config/isomorphic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,13 @@
* with the optimized React build, etc.
*/
export const IS_PRODUCTION_BUILD = process.env.NODE_ENV === "production";

/**
* Region-blocked requests will be redirected here. This is used in the
* middleware to implement the block, and also consumed in any components that
* are part of the page layout but need to know if the request is blocked from
* accessing the app, such as the WalletButton in the app header.
*
* Don't change unless you also change the relevant app route path to match.
*/
export const BLOCKED_SEGMENT = "blocked";
7 changes: 7 additions & 0 deletions apps/staking/src/config/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ export const WALLETCONNECT_PROJECT_ID = demandInProduction(
);
export const RPC = process.env.RPC;
export const IS_MAINNET = process.env.IS_MAINNET !== undefined;
export const BLOCKED_REGIONS =
process.env.BLOCKED_REGIONS === undefined ||
process.env.BLOCKED_REGIONS === ""
? []
: process.env.BLOCKED_REGIONS.split(",").map((region) =>
region.toLowerCase().trim(),
);

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

import { BLOCKED_SEGMENT } from "./config/isomorphic";
import { BLOCKED_REGIONS } 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));
} else {
return;
}
};

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

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