diff --git a/.github/workflows/deploy-cf-pages.yml b/.github/workflows/deploy-cf-pages.yml new file mode 100644 index 0000000..35ed627 --- /dev/null +++ b/.github/workflows/deploy-cf-pages.yml @@ -0,0 +1,39 @@ +name: Deploy to Cloudflare Pages + +on: + push: + branches: [main] + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + contents: read + deployments: write + pull-requests: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Build for Cloudflare Pages + run: npx @cloudflare/next-on-pages + + - name: Publish to Cloudflare Pages + uses: cloudflare/pages-action@v1 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + projectName: trustflow-frontend + directory: .vercel/output/static + gitHubToken: ${{ secrets.GITHUB_TOKEN }} + wranglerVersion: '3' diff --git a/next.config.js b/next.config.js index 54b36ce..5f3f217 100644 --- a/next.config.js +++ b/next.config.js @@ -1,10 +1,20 @@ +const { setupDevPlatform } = process.env.NODE_ENV === 'development' + ? require('@cloudflare/next-on-pages/next-dev') + : { setupDevPlatform: () => {} }; + /** @type {import('next').NextConfig} */ -module.exports = { +const nextConfig = { reactStrictMode: true, - // Built-in locale routing (Pages Router). Prefixes non-default locales, e.g. - // `/es/explore`, and exposes the active locale via `useRouter().locale`. + // Cloudflare Pages runs on the edge runtime which doesn't support the + // Node.js-based i18n middleware Next.js uses internally. We keep the + // locale list here for `useRouter().locale` support in the Pages Router + // and handle locale detection via a Cloudflare Pages _middleware instead. i18n: { locales: ['en', 'es'], defaultLocale: 'en', }, }; + +setupDevPlatform(); + +module.exports = nextConfig; diff --git a/pages/api/profile.ts b/pages/api/profile.ts index 6bb7729..89c8b81 100644 --- a/pages/api/profile.ts +++ b/pages/api/profile.ts @@ -1,4 +1,7 @@ -import type { NextApiRequest, NextApiResponse } from 'next' +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' + +export const runtime = 'edge' export interface ProfileGig { id: string @@ -25,10 +28,6 @@ export interface UserProfileResponse { source: 'backend' | 'mock' } -interface UserProfileError { - error: string -} - interface BackendProfileResponse { walletAddress: string displayName: string @@ -112,24 +111,19 @@ function normalizeBackendResponse(payload: BackendProfileResponse): UserProfileR } } -export default async function handler( - req: NextApiRequest, - res: NextApiResponse -) { +export default async function handler(req: NextRequest): Promise { if (req.method !== 'GET') { - return res.status(405).json({ error: 'Method not allowed' }) + return NextResponse.json({ error: 'Method not allowed' }, { status: 405 }) } - res.setHeader('Cache-Control', 's-maxage=30, stale-while-revalidate=120') + const { searchParams } = new URL(req.url) + const walletAddress = searchParams.get('walletAddress')?.trim() || 'unknown' - const walletAddress = - typeof req.query.walletAddress === 'string' && req.query.walletAddress.trim().length > 0 - ? req.query.walletAddress - : 'unknown' + const cacheHeaders = { 'Cache-Control': 's-maxage=30, stale-while-revalidate=120' } const backendBaseUrl = process.env.PROFILE_API_BASE_URL if (!backendBaseUrl) { - return res.status(200).json(getMockProfile(walletAddress)) + return NextResponse.json(getMockProfile(walletAddress), { headers: cacheHeaders }) } try { @@ -138,18 +132,16 @@ export default async function handler( const response = await fetch(url.toString(), { method: 'GET', - headers: { - Accept: 'application/json', - }, + headers: { Accept: 'application/json' }, }) if (!response.ok) { - return res.status(200).json(getMockProfile(walletAddress)) + return NextResponse.json(getMockProfile(walletAddress), { headers: cacheHeaders }) } const payload = (await response.json()) as BackendProfileResponse - return res.status(200).json(normalizeBackendResponse(payload)) + return NextResponse.json(normalizeBackendResponse(payload), { headers: cacheHeaders }) } catch { - return res.status(200).json(getMockProfile(walletAddress)) + return NextResponse.json(getMockProfile(walletAddress), { headers: cacheHeaders }) } } \ No newline at end of file diff --git a/pages/api/usdc-price.ts b/pages/api/usdc-price.ts index 389e3bc..882298c 100644 --- a/pages/api/usdc-price.ts +++ b/pages/api/usdc-price.ts @@ -1,36 +1,38 @@ -import type { NextApiRequest, NextApiResponse } from 'next' +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; + +export const runtime = 'edge'; export interface USDCPriceResponse { - price: number - currency: string - timestamp: number - source: string + price: number; + currency: string; + timestamp: number; + source: string; } export interface USDCPriceError { - error: string + error: string; } -// Simulate a realistic USDC peg: $1.00 ± 0.05% variance function getMockUSDCPrice(): number { - const variance = (Math.random() - 0.5) * 0.001 - return parseFloat((1.0 + variance).toFixed(4)) + const variance = (Math.random() - 0.5) * 0.001; + return parseFloat((1.0 + variance).toFixed(4)); } -export default function handler( - req: NextApiRequest, - res: NextApiResponse -) { +export default function handler(req: NextRequest): NextResponse { if (req.method !== 'GET') { - return res.status(405).json({ error: 'Method not allowed' }) + return NextResponse.json({ error: 'Method not allowed' }, { status: 405 }); } - res.setHeader('Cache-Control', 's-maxage=30, stale-while-revalidate=60') - - return res.status(200).json({ - price: getMockUSDCPrice(), - currency: 'USD', - timestamp: Date.now(), - source: 'mock', - }) + return NextResponse.json( + { + price: getMockUSDCPrice(), + currency: 'USD', + timestamp: Date.now(), + source: 'mock', + }, + { + headers: { 'Cache-Control': 's-maxage=30, stale-while-revalidate=60' }, + } + ); } diff --git a/wrangler.toml b/wrangler.toml new file mode 100644 index 0000000..f41e626 --- /dev/null +++ b/wrangler.toml @@ -0,0 +1,5 @@ +name = "trustflow-frontend" +compatibility_date = "2024-09-23" +compatibility_flags = ["nodejs_compat"] + +pages_build_output_dir = ".vercel/output/static"