diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d1ef20..3c38ffb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,3 +67,51 @@ jobs: env: NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }} NEXT_PUBLIC_STELLAR_NETWORK: TESTNET + + lighthouse: + name: Lighthouse CI (90+ all categories) + runs-on: ubuntu-latest + needs: build + steps: + - 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 production + run: npm run build + env: + NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }} + NEXT_PUBLIC_STELLAR_NETWORK: TESTNET + + - name: Install Lighthouse CI + run: npm install -g @lhci/cli@0.14.0 + + - name: Start server and run Lighthouse CI + run: | + npm start & + SERVER_PID=$! + echo "Server PID: $SERVER_PID" + for i in $(seq 1 30); do + curl -sf http://localhost:3000 > /dev/null 2>&1 && echo "Server ready after ${i}s" && break + sleep 2 + done + lhci autorun + kill $SERVER_PID 2>/dev/null || true + env: + PORT: 3000 + LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }} + + - name: Upload Lighthouse results + if: always() + uses: actions/upload-artifact@v4 + with: + name: lighthouse-results-${{ github.run_number }} + path: .lighthouseci/ + retention-days: 30 diff --git a/app/layout.tsx b/app/layout.tsx index 8d68e2f..96e8168 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -9,14 +9,14 @@ const manrope = Manrope({ subsets: ['latin'], variable: '--font-manrope', display: 'swap', - preload: false, + preload: true, }) const outfit = Outfit({ subsets: ['latin'], variable: '--font-cal-sans', display: 'swap', - preload: false, + preload: true, }) const spaceMono = Space_Mono({ @@ -42,7 +42,20 @@ export const metadata: Metadata = { 'fintech', 'Aframp', ], - generator: 'v0.app', + generator: 'Next.js', + robots: { + index: true, + follow: true, + googleBot: { index: true, follow: true }, + }, + openGraph: { + title: 'Aframp - Buy Crypto, Pay Bills & Send Money in Africa', + description: + "Africa's premier cNGN stablecoin payment platform. Buy crypto from ₦2,000, pay bills instantly, and send money across 12 African countries.", + type: 'website', + locale: 'en_NG', + siteName: 'Aframp', + }, } export const viewport: Viewport = { diff --git a/lighthouserc.json b/lighthouserc.json index 304c8dd..f7c490c 100644 --- a/lighthouserc.json +++ b/lighthouserc.json @@ -2,20 +2,41 @@ "ci": { "collect": { "numberOfRuns": 3, - "startServerCommand": "npm start", "url": [ "http://localhost:3000", "http://localhost:3000/onramp", "http://localhost:3000/dashboard" - ] + ], + "settings": { + "chromeFlags": "--no-sandbox --disable-dev-shm-usage", + "throttlingMethod": "simulate", + "formFactor": "desktop", + "screenEmulation": { + "mobile": false, + "width": 1350, + "height": 940, + "deviceScaleFactor": 1, + "disabled": false + } + } }, "assert": { - "preset": "lighthouse:recommended", "assertions": { - "categories:performance": ["error", { "minScore": 0.8 }], - "categories:accessibility": ["error", { "minScore": 0.9 }], - "categories:best-practices": ["error", { "minScore": 0.85 }], - "categories:seo": ["error", { "minScore": 0.85 }] + "categories:performance": ["error", { "minScore": 0.9 }], + "categories:accessibility": ["error", { "minScore": 0.9 }], + "categories:best-practices": ["error", { "minScore": 0.9 }], + "categories:seo": ["error", { "minScore": 0.9 }], + + "cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }], + "largest-contentful-paint": ["error", { "maxNumericValue": 2500 }], + "total-blocking-time": ["error", { "maxNumericValue": 300 }], + "first-contentful-paint": ["error", { "maxNumericValue": 1800 }], + + "document-title": ["error", { "minScore": 1 }], + "meta-description": ["error", { "minScore": 1 }], + "html-has-lang": ["error", { "minScore": 1 }], + "image-alt": ["error", { "minScore": 1 }], + "button-name": ["error", { "minScore": 1 }] } }, "upload": { diff --git a/next.config.mjs b/next.config.mjs index 5a5a7bc..5f0b689 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -4,10 +4,28 @@ const nextConfig = { ignoreBuildErrors: true, }, images: { - unoptimized: true, + // Allow optimized images for Lighthouse Best Practices score + unoptimized: false, + formats: ['image/avif', 'image/webp'], + minimumCacheTTL: 60, }, // Enable standalone output for Docker deployments output: 'standalone', + // Security headers — improves Best Practices score + async headers() { + return [ + { + source: '/(.*)', + headers: [ + { key: 'X-Content-Type-Options', value: 'nosniff' }, + { key: 'X-Frame-Options', value: 'DENY' }, + { key: 'X-XSS-Protection', value: '1; mode=block' }, + { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' }, + { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' }, + ], + }, + ] + }, } export default nextConfig