diff --git a/package.json b/package.json index 972720d..6e3ffdd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@neynar/create-farcaster-mini-app", - "version": "1.7.0", + "version": "1.7.1", "type": "module", "private": false, "access": "public", diff --git a/src/app/app/.well-known/farcaster.json/route.ts b/src/app/app/.well-known/farcaster.json/route.ts deleted file mode 100644 index d116c4f..0000000 --- a/src/app/app/.well-known/farcaster.json/route.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NextResponse } from 'next/server'; -import { getFarcasterDomainManifest } from '~/lib/utils'; - -export async function GET() { - try { - const config = await getFarcasterDomainManifest(); - return NextResponse.json(config); - } catch (error) { - console.error('Error generating metadata:', error); - return NextResponse.json({ error: error.message }, { status: 500 }); - } -} diff --git a/src/app/app/api/auth/nonce/route.ts b/src/app/app/api/auth/nonce/route.ts deleted file mode 100644 index a1f25ea..0000000 --- a/src/app/app/api/auth/nonce/route.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { NextResponse } from 'next/server'; -import { getNeynarClient } from '~/lib/neynar'; - -export async function GET() { - try { - const client = getNeynarClient(); - const response = await client.fetchNonce(); - return NextResponse.json(response); - } catch (error) { - console.error('Error fetching nonce:', error); - return NextResponse.json( - { error: 'Failed to fetch nonce' }, - { status: 500 } - ); - } -} diff --git a/src/app/app/api/auth/session-signers/route.ts b/src/app/app/api/auth/session-signers/route.ts deleted file mode 100644 index 630ef3b..0000000 --- a/src/app/app/api/auth/session-signers/route.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { NextResponse } from 'next/server'; -import { getNeynarClient } from '~/lib/neynar'; - -export async function GET(request: Request) { - try { - const { searchParams } = new URL(request.url); - const message = searchParams.get('message'); - const signature = searchParams.get('signature'); - - if (!message || !signature) { - return NextResponse.json( - { error: 'Message and signature are required' }, - { status: 400 } - ); - } - - const client = getNeynarClient(); - const data = await client.fetchSigners({ message, signature }); - const signers = data.signers; - - // Fetch user data if signers exist - let user = null; - if (signers && signers.length > 0 && signers[0].fid) { - const { - users: [fetchedUser], - } = await client.fetchBulkUsers({ - fids: [signers[0].fid], - }); - user = fetchedUser; - } - - return NextResponse.json({ - signers, - user, - }); - } catch (error) { - console.error('Error in session-signers API:', error); - return NextResponse.json( - { error: 'Failed to fetch signers' }, - { status: 500 } - ); - } -} diff --git a/src/app/app/api/auth/signer/route.ts b/src/app/app/api/auth/signer/route.ts deleted file mode 100644 index f793d0e..0000000 --- a/src/app/app/api/auth/signer/route.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { NextResponse } from 'next/server'; -import { getNeynarClient } from '~/lib/neynar'; - -export async function POST() { - try { - const neynarClient = getNeynarClient(); - const signer = await neynarClient.createSigner(); - return NextResponse.json(signer); - } catch (error) { - console.error('Error fetching signer:', error); - return NextResponse.json( - { error: 'Failed to fetch signer' }, - { status: 500 } - ); - } -} - -export async function GET(request: Request) { - const { searchParams } = new URL(request.url); - const signerUuid = searchParams.get('signerUuid'); - - if (!signerUuid) { - return NextResponse.json( - { error: 'signerUuid is required' }, - { status: 400 } - ); - } - - try { - const neynarClient = getNeynarClient(); - const signer = await neynarClient.lookupSigner({ - signerUuid, - }); - return NextResponse.json(signer); - } catch (error) { - console.error('Error fetching signed key:', error); - return NextResponse.json( - { error: 'Failed to fetch signed key' }, - { status: 500 } - ); - } -} diff --git a/src/app/app/api/auth/signer/signed_key/route.ts b/src/app/app/api/auth/signer/signed_key/route.ts deleted file mode 100644 index d7a3df8..0000000 --- a/src/app/app/api/auth/signer/signed_key/route.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { NextResponse } from 'next/server'; -import { getNeynarClient } from '~/lib/neynar'; -import { mnemonicToAccount } from 'viem/accounts'; -import { - SIGNED_KEY_REQUEST_TYPE, - SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN, -} from '~/lib/constants'; - -const postRequiredFields = ['signerUuid', 'publicKey']; - -export async function POST(request: Request) { - const body = await request.json(); - - // Validate required fields - for (const field of postRequiredFields) { - if (!body[field]) { - return NextResponse.json( - { error: `${field} is required` }, - { status: 400 } - ); - } - } - - const { signerUuid, publicKey, redirectUrl } = body; - - if (redirectUrl && typeof redirectUrl !== 'string') { - return NextResponse.json( - { error: 'redirectUrl must be a string' }, - { status: 400 } - ); - } - - try { - // Get the app's account from seed phrase - const seedPhrase = process.env.SEED_PHRASE; - const shouldSponsor = process.env.SPONSOR_SIGNER === 'true'; - - if (!seedPhrase) { - return NextResponse.json( - { error: 'App configuration missing (SEED_PHRASE or FID)' }, - { status: 500 } - ); - } - - const neynarClient = getNeynarClient(); - - const account = mnemonicToAccount(seedPhrase); - - const { - user: { fid }, - } = await neynarClient.lookupUserByCustodyAddress({ - custodyAddress: account.address, - }); - - const appFid = fid; - - // Generate deadline (24 hours from now) - const deadline = Math.floor(Date.now() / 1000) + 86400; - - // Generate EIP-712 signature - const signature = await account.signTypedData({ - domain: SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN, - types: { - SignedKeyRequest: SIGNED_KEY_REQUEST_TYPE, - }, - primaryType: 'SignedKeyRequest', - message: { - requestFid: BigInt(appFid), - key: publicKey, - deadline: BigInt(deadline), - }, - }); - - const signer = await neynarClient.registerSignedKey({ - appFid, - deadline, - signature, - signerUuid, - ...(redirectUrl && { redirectUrl }), - ...(shouldSponsor && { sponsor: { sponsored_by_neynar: true } }), - }); - - return NextResponse.json(signer); - } catch (error) { - console.error('Error registering signed key:', error); - return NextResponse.json( - { error: 'Failed to register signed key' }, - { status: 500 } - ); - } -} diff --git a/src/app/app/api/auth/signers/route.ts b/src/app/app/api/auth/signers/route.ts deleted file mode 100644 index 1c89acf..0000000 --- a/src/app/app/api/auth/signers/route.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { NextResponse } from 'next/server'; -import { getNeynarClient } from '~/lib/neynar'; - -const requiredParams = ['message', 'signature']; - -export async function GET(request: Request) { - const { searchParams } = new URL(request.url); - const params: Record = {}; - for (const param of requiredParams) { - params[param] = searchParams.get(param); - if (!params[param]) { - return NextResponse.json( - { - error: `${param} parameter is required`, - }, - { status: 400 } - ); - } - } - - const message = params.message as string; - const signature = params.signature as string; - - try { - const client = getNeynarClient(); - const data = await client.fetchSigners({ message, signature }); - const signers = data.signers; - return NextResponse.json({ - signers, - }); - } catch (error) { - console.error('Error fetching signers:', error); - return NextResponse.json( - { error: 'Failed to fetch signers' }, - { status: 500 } - ); - } -} diff --git a/src/app/app/api/auth/validate/route.ts b/src/app/app/api/auth/validate/route.ts deleted file mode 100644 index 70256f8..0000000 --- a/src/app/app/api/auth/validate/route.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { NextResponse } from 'next/server'; -import { createClient, Errors } from '@farcaster/quick-auth'; - -const client = createClient(); - -export async function POST(request: Request) { - try { - const { token } = await request.json(); - - if (!token) { - return NextResponse.json( - { error: 'Token is required' }, - { status: 400 } - ); - } - - // Get domain from environment or request - const domain = process.env.NEXT_PUBLIC_URL - ? new URL(process.env.NEXT_PUBLIC_URL).hostname - : request.headers.get('host') || 'localhost'; - - try { - // Use the official QuickAuth library to verify the JWT - const payload = await client.verifyJwt({ - token, - domain, - }); - - return NextResponse.json({ - success: true, - user: { - fid: payload.sub, - }, - }); - } catch (e) { - if (e instanceof Errors.InvalidTokenError) { - console.info('Invalid token:', e.message); - return NextResponse.json( - { error: 'Invalid token' }, - { status: 401 } - ); - } - throw e; - } - } catch (error) { - console.error('Token validation error:', error); - return NextResponse.json( - { error: 'Internal server error' }, - { status: 500 } - ); - } -} \ No newline at end of file diff --git a/src/app/app/api/best-friends/route.ts b/src/app/app/api/best-friends/route.ts deleted file mode 100644 index 925724f..0000000 --- a/src/app/app/api/best-friends/route.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { NextResponse } from 'next/server'; - -export async function GET(request: Request) { - const apiKey = process.env.NEYNAR_API_KEY; - const { searchParams } = new URL(request.url); - const fid = searchParams.get('fid'); - - if (!apiKey) { - return NextResponse.json( - { error: 'Neynar API key is not configured. Please add NEYNAR_API_KEY to your environment variables.' }, - { status: 500 } - ); - } - - if (!fid) { - return NextResponse.json( - { error: 'FID parameter is required' }, - { status: 400 } - ); - } - - try { - const response = await fetch( - `https://api.neynar.com/v2/farcaster/user/best_friends?fid=${fid}&limit=3`, - { - headers: { - "x-api-key": apiKey, - }, - } - ); - - if (!response.ok) { - throw new Error(`Neynar API error: ${response.statusText}`); - } - - const { users } = await response.json() as { users: { user: { fid: number; username: string } }[] }; - - return NextResponse.json({ bestFriends: users }); - } catch (error) { - console.error('Failed to fetch best friends:', error); - return NextResponse.json( - { error: 'Failed to fetch best friends. Please check your Neynar API key and try again.' }, - { status: 500 } - ); - } -} \ No newline at end of file diff --git a/src/app/app/api/opengraph-image/route.tsx b/src/app/app/api/opengraph-image/route.tsx deleted file mode 100644 index b14415f..0000000 --- a/src/app/app/api/opengraph-image/route.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { ImageResponse } from "next/og"; -import { NextRequest } from "next/server"; -import { getNeynarUser } from "~/lib/neynar"; - -export const dynamic = 'force-dynamic'; - -export async function GET(request: NextRequest) { - const { searchParams } = new URL(request.url); - const fid = searchParams.get('fid'); - - const user = fid ? await getNeynarUser(Number(fid)) : null; - - return new ImageResponse( - ( -
- {user?.pfp_url && ( -
- Profile -
- )} -

{user?.display_name ? `Hello from ${user.display_name ?? user.username}!` : 'Hello!'}

-

Powered by Neynar 🪐

-
- ), - { - width: 1200, - height: 800, - } - ); -} \ No newline at end of file diff --git a/src/app/app/api/send-notification/route.ts b/src/app/app/api/send-notification/route.ts deleted file mode 100644 index 7563723..0000000 --- a/src/app/app/api/send-notification/route.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { notificationDetailsSchema } from "@farcaster/miniapp-sdk"; -import { NextRequest } from "next/server"; -import { z } from "zod"; -import { setUserNotificationDetails } from "~/lib/kv"; -import { sendMiniAppNotification } from "~/lib/notifs"; -import { sendNeynarMiniAppNotification } from "~/lib/neynar"; - -const requestSchema = z.object({ - fid: z.number(), - notificationDetails: notificationDetailsSchema, -}); - -export async function POST(request: NextRequest) { - // If Neynar is enabled, we don't need to store notification details - // as they will be managed by Neynar's system - const neynarEnabled = process.env.NEYNAR_API_KEY && process.env.NEYNAR_CLIENT_ID; - - const requestJson = await request.json(); - const requestBody = requestSchema.safeParse(requestJson); - - if (requestBody.success === false) { - return Response.json( - { success: false, errors: requestBody.error.errors }, - { status: 400 } - ); - } - - // Only store notification details if not using Neynar - if (!neynarEnabled) { - await setUserNotificationDetails( - Number(requestBody.data.fid), - requestBody.data.notificationDetails - ); - } - - // Use appropriate notification function based on Neynar status - const sendNotification = neynarEnabled ? sendNeynarMiniAppNotification : sendMiniAppNotification; - const sendResult = await sendNotification({ - fid: Number(requestBody.data.fid), - title: "Test notification", - body: "Sent at " + new Date().toISOString(), - }); - - if (sendResult.state === "error") { - return Response.json( - { success: false, error: sendResult.error }, - { status: 500 } - ); - } else if (sendResult.state === "rate_limit") { - return Response.json( - { success: false, error: "Rate limited" }, - { status: 429 } - ); - } - - return Response.json({ success: true }); -} diff --git a/src/app/app/api/users/route.ts b/src/app/app/api/users/route.ts deleted file mode 100644 index cca1f37..0000000 --- a/src/app/app/api/users/route.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { NeynarAPIClient } from '@neynar/nodejs-sdk'; -import { NextResponse } from 'next/server'; - -export async function GET(request: Request) { - const apiKey = process.env.NEYNAR_API_KEY; - const { searchParams } = new URL(request.url); - const fids = searchParams.get('fids'); - - if (!apiKey) { - return NextResponse.json( - { error: 'Neynar API key is not configured. Please add NEYNAR_API_KEY to your environment variables.' }, - { status: 500 } - ); - } - - if (!fids) { - return NextResponse.json( - { error: 'FIDs parameter is required' }, - { status: 400 } - ); - } - - try { - const neynar = new NeynarAPIClient({ apiKey }); - const fidsArray = fids.split(',').map(fid => parseInt(fid.trim())); - - const { users } = await neynar.fetchBulkUsers({ - fids: fidsArray, - }); - - return NextResponse.json({ users }); - } catch (error) { - console.error('Failed to fetch users:', error); - return NextResponse.json( - { error: 'Failed to fetch users. Please check your Neynar API key and try again.' }, - { status: 500 } - ); - } -} diff --git a/src/app/app/api/webhook/route.ts b/src/app/app/api/webhook/route.ts deleted file mode 100644 index cc49676..0000000 --- a/src/app/app/api/webhook/route.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { - ParseWebhookEvent, - parseWebhookEvent, - verifyAppKeyWithNeynar, -} from "@farcaster/miniapp-node"; -import { NextRequest } from "next/server"; -import { APP_NAME } from "~/lib/constants"; -import { - deleteUserNotificationDetails, - setUserNotificationDetails, -} from "~/lib/kv"; -import { sendMiniAppNotification } from "~/lib/notifs"; - -export async function POST(request: NextRequest) { - // If Neynar is enabled, we don't need to handle webhooks here - // as they will be handled by Neynar's webhook endpoint - const neynarEnabled = process.env.NEYNAR_API_KEY && process.env.NEYNAR_CLIENT_ID; - if (neynarEnabled) { - return Response.json({ success: true }); - } - - const requestJson = await request.json(); - - let data; - try { - data = await parseWebhookEvent(requestJson, verifyAppKeyWithNeynar); - } catch (e: unknown) { - const error = e as ParseWebhookEvent.ErrorType; - - switch (error.name) { - case "VerifyJsonFarcasterSignature.InvalidDataError": - case "VerifyJsonFarcasterSignature.InvalidEventDataError": - // The request data is invalid - return Response.json( - { success: false, error: error.message }, - { status: 400 } - ); - case "VerifyJsonFarcasterSignature.InvalidAppKeyError": - // The app key is invalid - return Response.json( - { success: false, error: error.message }, - { status: 401 } - ); - case "VerifyJsonFarcasterSignature.VerifyAppKeyError": - // Internal error verifying the app key (caller may want to try again) - return Response.json( - { success: false, error: error.message }, - { status: 500 } - ); - } - } - - const fid = data.fid; - const event = data.event; - - // Only handle notifications if Neynar is not enabled - // When Neynar is enabled, notifications are handled through their webhook - switch (event.event) { - case "frame_added": - if (event.notificationDetails) { - await setUserNotificationDetails(fid, event.notificationDetails); - await sendMiniAppNotification({ - fid, - title: `Welcome to ${APP_NAME}`, - body: "Mini app is now added to your client", - }); - } else { - await deleteUserNotificationDetails(fid); - } - break; - - case "frame_removed": - await deleteUserNotificationDetails(fid); - break; - - case "notifications_enabled": - await setUserNotificationDetails(fid, event.notificationDetails); - await sendMiniAppNotification({ - fid, - title: `Welcome to ${APP_NAME}`, - body: "Notifications are now enabled", - }); - break; - - case "notifications_disabled": - await deleteUserNotificationDetails(fid); - break; - } - - return Response.json({ success: true }); -} diff --git a/src/app/app/app.tsx b/src/app/app/app.tsx deleted file mode 100644 index c9d7d23..0000000 --- a/src/app/app/app.tsx +++ /dev/null @@ -1,15 +0,0 @@ -"use client"; - -import dynamic from "next/dynamic"; -import { APP_NAME } from "~/lib/constants"; - -// note: dynamic import is required for components that use the Frame SDK -const AppComponent = dynamic(() => import("~/components/App"), { - ssr: false, -}); - -export default function App( - { title }: { title?: string } = { title: APP_NAME } -) { - return ; -} diff --git a/src/app/app/favicon.ico b/src/app/app/favicon.ico deleted file mode 100644 index afa21c9..0000000 Binary files a/src/app/app/favicon.ico and /dev/null differ diff --git a/src/app/app/globals.css b/src/app/app/globals.css deleted file mode 100644 index 77147d0..0000000 --- a/src/app/app/globals.css +++ /dev/null @@ -1,118 +0,0 @@ -/** - * DESIGN SYSTEM - DO NOT EDIT UNLESS NECESSARY - * - * This file contains the centralized design system for the mini app. - * These component classes establish the visual consistency across all components. - * - * ⚠️ AI SHOULD NOT NORMALLY EDIT THIS FILE ⚠️ - * - * Instead of modifying these classes, AI should: - * 1. Use existing component classes (e.g., .btn, .card, .input) - * 2. Use Tailwind utilities for one-off styling - * 3. Create new React components rather than new CSS classes - * 4. Only edit this file for specific bug fixes or accessibility improvements - * - * When AI needs to style something: - * ✅ Good: - * ✅ Good:
Custom
- * ❌ Bad: Adding new CSS classes here for component-specific styling - * - * This design system is intentionally minimal to prevent bloat and maintain consistency. - */ - -@tailwind base; -@tailwind components; -@tailwind utilities; - -:root { - --background: #ffffff; - --foreground: #171717; -} - -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } -} - -body { - color: var(--foreground); - background: var(--background); - font-family: 'Inter', Helvetica, Arial, sans-serif; -} - -* { - scrollbar-width: none; /* Firefox */ - -ms-overflow-style: none; /* IE and Edge */ -} - -*::-webkit-scrollbar { - display: none; -} - -@layer base { - :root { - --radius: 0.5rem; - } -} - -@layer components { - /* Global container styles for consistent layout */ - .container { - @apply mx-auto max-w-md px-4; - } - - .container-wide { - @apply mx-auto max-w-lg px-4; - } - - .container-narrow { - @apply mx-auto max-w-sm px-4; - } - - /* Global card styles */ - .card { - @apply bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 shadow-sm; - } - - .card-primary { - @apply bg-primary/10 border-primary/20; - } - - /* Global button styles */ - .btn { - @apply inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none; - } - - .btn-primary { - @apply bg-primary text-white hover:bg-primary-dark focus:ring-primary; - } - - .btn-secondary { - @apply bg-secondary text-gray-900 hover:bg-gray-200 focus:ring-gray-500 dark:bg-secondary-dark dark:text-gray-100 dark:hover:bg-gray-600; - } - - .btn-outline { - @apply border border-gray-300 bg-transparent hover:bg-gray-50 focus:ring-gray-500 dark:border-gray-600 dark:hover:bg-gray-800; - } - - /* Global input styles */ - .input { - @apply block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-gray-900 placeholder-gray-500 focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400; - } - - /* Global loading spinner */ - .spinner { - @apply animate-spin rounded-full border-2 border-gray-300 border-t-primary; - } - - .spinner-primary { - @apply animate-spin rounded-full border-2 border-white border-t-transparent; - } - - /* Global focus styles */ - .focus-ring { - @apply focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2; - } -} diff --git a/src/app/app/layout.tsx b/src/app/app/layout.tsx deleted file mode 100644 index acf3b41..0000000 --- a/src/app/app/layout.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type { Metadata } from "next"; - -import "~/app/globals.css"; -import { Providers } from "~/app/providers"; -import { APP_NAME, APP_DESCRIPTION } from "~/lib/constants"; - -export const metadata: Metadata = { - title: APP_NAME, - description: APP_DESCRIPTION, -}; - -export default async function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - {children} - - - ); -} diff --git a/src/app/app/page.tsx b/src/app/app/page.tsx deleted file mode 100644 index 4e11816..0000000 --- a/src/app/app/page.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Metadata } from "next"; -import App from "./app"; -import { APP_NAME, APP_DESCRIPTION, APP_OG_IMAGE_URL } from "~/lib/constants"; -import { getMiniAppEmbedMetadata } from "~/lib/utils"; - -export const revalidate = 300; - -export async function generateMetadata(): Promise { - return { - title: APP_NAME, - openGraph: { - title: APP_NAME, - description: APP_DESCRIPTION, - images: [APP_OG_IMAGE_URL], - }, - other: { - "fc:frame": JSON.stringify(getMiniAppEmbedMetadata()), - }, - }; -} - -export default function Home() { - return (); -} diff --git a/src/app/app/providers.tsx b/src/app/app/providers.tsx deleted file mode 100644 index b5884a5..0000000 --- a/src/app/app/providers.tsx +++ /dev/null @@ -1,34 +0,0 @@ -'use client'; - -import dynamic from 'next/dynamic'; -import { MiniAppProvider } from '@neynar/react'; -import { SafeFarcasterSolanaProvider } from '~/components/providers/SafeFarcasterSolanaProvider'; -import { ANALYTICS_ENABLED } from '~/lib/constants'; - -const WagmiProvider = dynamic( - () => import('~/components/providers/WagmiProvider'), - { - ssr: false, - } -); - -export function Providers({ - children, -}: { - children: React.ReactNode; -}) { - const solanaEndpoint = - process.env.SOLANA_RPC_ENDPOINT || 'https://solana-rpc.publicnode.com'; - return ( - - - - {children} - - - - ); -} diff --git a/src/app/app/share/[fid]/page.tsx b/src/app/app/share/[fid]/page.tsx deleted file mode 100644 index 861c3cf..0000000 --- a/src/app/app/share/[fid]/page.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type { Metadata } from "next"; -import { redirect } from "next/navigation"; -import { APP_URL, APP_NAME, APP_DESCRIPTION } from "~/lib/constants"; -import { getMiniAppEmbedMetadata } from "~/lib/utils"; -export const revalidate = 300; - -// This is an example of how to generate a dynamically generated share page based on fid: -// Sharing this route e.g. exmaple.com/share/123 will generate a share page for fid 123, -// with the image dynamically generated by the opengraph-image API route. -export async function generateMetadata({ - params, -}: { - params: Promise<{ fid: string }>; -}): Promise { - const { fid } = await params; - const imageUrl = `${APP_URL}/api/opengraph-image?fid=${fid}`; - - return { - title: `${APP_NAME} - Share`, - openGraph: { - title: APP_NAME, - description: APP_DESCRIPTION, - images: [imageUrl], - }, - other: { - "fc:frame": JSON.stringify(getMiniAppEmbedMetadata(imageUrl)), - }, - }; -} - -export default function SharePage() { - // redirect to home page - redirect("/"); -}