mirror of
https://github.com/neynarxyz/create-farcaster-mini-app.git
synced 2025-11-15 23:58:56 -05:00
format
This commit is contained in:
parent
b366d97b53
commit
378ea65154
14
bin/init.js
14
bin/init.js
@ -1,12 +1,12 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import inquirer from 'inquirer';
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
import { dirname } from 'path';
|
|
||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
|
import fs from 'fs';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import path from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import inquirer from 'inquirer';
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = dirname(__filename);
|
const __dirname = dirname(__filename);
|
||||||
@ -47,7 +47,7 @@ async function queryNeynarApp(apiKey) {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`https://api.neynar.com/portal/app_by_api_key?starter_kit=true`,
|
'https://api.neynar.com/portal/app_by_api_key?starter_kit=true',
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'x-api-key': apiKey,
|
'x-api-key': apiKey,
|
||||||
@ -444,7 +444,7 @@ export async function init(
|
|||||||
// Update package.json
|
// Update package.json
|
||||||
console.log('\nUpdating package.json...');
|
console.log('\nUpdating package.json...');
|
||||||
const packageJsonPath = path.join(projectPath, 'package.json');
|
const packageJsonPath = path.join(projectPath, 'package.json');
|
||||||
let packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||||
|
|
||||||
packageJson.name = finalProjectName;
|
packageJson.name = finalProjectName;
|
||||||
packageJson.version = '0.1.0';
|
packageJson.version = '0.1.0';
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import localtunnel from 'localtunnel';
|
|
||||||
import { spawn } from 'child_process';
|
import { spawn } from 'child_process';
|
||||||
import { createServer } from 'net';
|
import { createServer } from 'net';
|
||||||
import dotenv from 'dotenv';
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
import localtunnel from 'localtunnel';
|
||||||
|
|
||||||
// Load environment variables
|
// Load environment variables
|
||||||
dotenv.config({ path: '.env.local' });
|
dotenv.config({ path: '.env.local' });
|
||||||
@ -96,7 +96,7 @@ async function startDev() {
|
|||||||
? `1. Run: netstat -ano | findstr :${port}\n` +
|
? `1. Run: netstat -ano | findstr :${port}\n` +
|
||||||
'2. Note the PID (Process ID) from the output\n' +
|
'2. Note the PID (Process ID) from the output\n' +
|
||||||
'3. Run: taskkill /PID <PID> /F\n'
|
'3. Run: taskkill /PID <PID> /F\n'
|
||||||
: `On macOS/Linux, run:\nnpm run cleanup\n`) +
|
: 'On macOS/Linux, run:\nnpm run cleanup\n') +
|
||||||
'\nThen try running this command again.',
|
'\nThen try running this command again.',
|
||||||
);
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|||||||
@ -8,15 +8,12 @@ export async function POST(request: Request) {
|
|||||||
const { token } = await request.json();
|
const { token } = await request.json();
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
return NextResponse.json(
|
return NextResponse.json({ error: 'Token is required' }, { status: 400 });
|
||||||
{ error: 'Token is required' },
|
|
||||||
{ status: 400 }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get domain from environment or request
|
// Get domain from environment or request
|
||||||
const domain = process.env.NEXT_PUBLIC_URL
|
const domain = process.env.NEXT_PUBLIC_URL
|
||||||
? new URL(process.env.NEXT_PUBLIC_URL).hostname
|
? new URL(process.env.NEXT_PUBLIC_URL).hostname
|
||||||
: request.headers.get('host') || 'localhost';
|
: request.headers.get('host') || 'localhost';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -35,10 +32,7 @@ export async function POST(request: Request) {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Errors.InvalidTokenError) {
|
if (e instanceof Errors.InvalidTokenError) {
|
||||||
console.info('Invalid token:', e.message);
|
console.info('Invalid token:', e.message);
|
||||||
return NextResponse.json(
|
return NextResponse.json({ error: 'Invalid token' }, { status: 401 });
|
||||||
{ error: 'Invalid token' },
|
|
||||||
{ status: 401 }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@ -46,7 +40,7 @@ export async function POST(request: Request) {
|
|||||||
console.error('Token validation error:', error);
|
console.error('Token validation error:', error);
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: 'Internal server error' },
|
{ error: 'Internal server error' },
|
||||||
{ status: 500 }
|
{ status: 500 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
|
|
||||||
import '~/app/globals.css';
|
import '~/app/globals.css';
|
||||||
import { Providers } from '~/app/providers';
|
import { Providers } from '~/app/providers';
|
||||||
import { APP_NAME, APP_DESCRIPTION } from '~/lib/constants';
|
import { APP_NAME, APP_DESCRIPTION } from '~/lib/constants';
|
||||||
|
|||||||
@ -16,8 +16,14 @@ export function ProfileButton({
|
|||||||
|
|
||||||
useDetectClickOutside(ref, () => setShowDropdown(false));
|
useDetectClickOutside(ref, () => setShowDropdown(false));
|
||||||
|
|
||||||
const name = userData?.username && userData.username.trim() !== '' ? userData.username : `!${userData?.fid}`;
|
const name =
|
||||||
const pfpUrl = userData?.pfpUrl && userData.pfpUrl.trim() !== '' ? userData.pfpUrl : 'https://farcaster.xyz/avatar.png';
|
userData?.username && userData.username.trim() !== ''
|
||||||
|
? userData.username
|
||||||
|
: `!${userData?.fid}`;
|
||||||
|
const pfpUrl =
|
||||||
|
userData?.pfpUrl && userData.pfpUrl.trim() !== ''
|
||||||
|
? userData.pfpUrl
|
||||||
|
: 'https://farcaster.xyz/avatar.png';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative" ref={ref}>
|
<div className="relative" ref={ref}>
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useCallback, useEffect, useState, useRef } from 'react';
|
import { useCallback, useEffect, useState, useRef } from 'react';
|
||||||
import { cn } from '~/lib/utils';
|
|
||||||
import { Button } from '~/components/ui/Button';
|
|
||||||
import { ProfileButton } from '~/components/ui/NeynarAuthButton/ProfileButton';
|
|
||||||
import { AuthDialog } from '~/components/ui/NeynarAuthButton/AuthDialog';
|
|
||||||
import { useMiniApp } from '@neynar/react';
|
|
||||||
import sdk, { SignIn as SignInCore } from '@farcaster/frame-sdk';
|
import sdk, { SignIn as SignInCore } from '@farcaster/frame-sdk';
|
||||||
|
import { useMiniApp } from '@neynar/react';
|
||||||
|
import { Button } from '~/components/ui/Button';
|
||||||
|
import { AuthDialog } from '~/components/ui/NeynarAuthButton/AuthDialog';
|
||||||
|
import { ProfileButton } from '~/components/ui/NeynarAuthButton/ProfileButton';
|
||||||
import { useQuickAuth } from '~/hooks/useQuickAuth';
|
import { useQuickAuth } from '~/hooks/useQuickAuth';
|
||||||
|
import { cn } from '~/lib/utils';
|
||||||
|
|
||||||
type User = {
|
type User = {
|
||||||
fid: number;
|
fid: number;
|
||||||
@ -142,7 +142,7 @@ export function NeynarAuthButton() {
|
|||||||
const updateSessionWithSigners = useCallback(
|
const updateSessionWithSigners = useCallback(
|
||||||
async (
|
async (
|
||||||
signers: StoredAuthState['signers'],
|
signers: StoredAuthState['signers'],
|
||||||
user: StoredAuthState['user'],
|
_user: StoredAuthState['user'],
|
||||||
) => {
|
) => {
|
||||||
if (!useBackendFlow) return;
|
if (!useBackendFlow) return;
|
||||||
|
|
||||||
@ -392,7 +392,7 @@ export function NeynarAuthButton() {
|
|||||||
setMessage(result.message);
|
setMessage(result.message);
|
||||||
setSignature(result.signature);
|
setSignature(result.signature);
|
||||||
// Use QuickAuth to sign in
|
// Use QuickAuth to sign in
|
||||||
const signInResult = await quickAuthSignIn();
|
await quickAuthSignIn();
|
||||||
// Fetch user profile after sign in
|
// Fetch user profile after sign in
|
||||||
if (quickAuthUser?.fid) {
|
if (quickAuthUser?.fid) {
|
||||||
const user = await fetchUserData(quickAuthUser.fid);
|
const user = await fetchUserData(quickAuthUser.fid);
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import sdk, { SignIn as SignInCore } from '@farcaster/miniapp-sdk';
|
import { SignIn as SignInCore } from '@farcaster/miniapp-sdk';
|
||||||
import { Button } from '../Button';
|
|
||||||
import { useQuickAuth } from '~/hooks/useQuickAuth';
|
import { useQuickAuth } from '~/hooks/useQuickAuth';
|
||||||
|
import { Button } from '../Button';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SignIn component handles Farcaster authentication using QuickAuth.
|
* SignIn component handles Farcaster authentication using QuickAuth.
|
||||||
|
|||||||
@ -34,25 +34,25 @@ interface UseQuickAuthReturn {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom hook for managing QuickAuth authentication state
|
* Custom hook for managing QuickAuth authentication state
|
||||||
*
|
*
|
||||||
* This hook provides a complete authentication flow using Farcaster's QuickAuth:
|
* This hook provides a complete authentication flow using Farcaster's QuickAuth:
|
||||||
* - Automatically checks for existing authentication on mount
|
* - Automatically checks for existing authentication on mount
|
||||||
* - Validates tokens with the server-side API
|
* - Validates tokens with the server-side API
|
||||||
* - Manages authentication state in memory (no persistence)
|
* - Manages authentication state in memory (no persistence)
|
||||||
* - Provides sign-in/sign-out functionality
|
* - Provides sign-in/sign-out functionality
|
||||||
*
|
*
|
||||||
* QuickAuth tokens are managed in memory only, so signing out of the Farcaster
|
* QuickAuth tokens are managed in memory only, so signing out of the Farcaster
|
||||||
* client will automatically sign the user out of this mini app as well.
|
* client will automatically sign the user out of this mini app as well.
|
||||||
*
|
*
|
||||||
* @returns {UseQuickAuthReturn} Object containing user state and authentication methods
|
* @returns {UseQuickAuthReturn} Object containing user state and authentication methods
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```tsx
|
* ```tsx
|
||||||
* const { authenticatedUser, status, signIn, signOut } = useQuickAuth();
|
* const { authenticatedUser, status, signIn, signOut } = useQuickAuth();
|
||||||
*
|
*
|
||||||
* if (status === 'loading') return <div>Loading...</div>;
|
* if (status === 'loading') return <div>Loading...</div>;
|
||||||
* if (status === 'unauthenticated') return <button onClick={signIn}>Sign In</button>;
|
* if (status === 'unauthenticated') return <button onClick={signIn}>Sign In</button>;
|
||||||
*
|
*
|
||||||
* return (
|
* return (
|
||||||
* <div>
|
* <div>
|
||||||
* <p>Welcome, FID: {authenticatedUser?.fid}</p>
|
* <p>Welcome, FID: {authenticatedUser?.fid}</p>
|
||||||
@ -63,17 +63,20 @@ interface UseQuickAuthReturn {
|
|||||||
*/
|
*/
|
||||||
export function useQuickAuth(): UseQuickAuthReturn {
|
export function useQuickAuth(): UseQuickAuthReturn {
|
||||||
// Current authenticated user data
|
// Current authenticated user data
|
||||||
const [authenticatedUser, setAuthenticatedUser] = useState<AuthenticatedUser | null>(null);
|
const [authenticatedUser, setAuthenticatedUser] =
|
||||||
|
useState<AuthenticatedUser | null>(null);
|
||||||
// Current authentication status
|
// Current authentication status
|
||||||
const [status, setStatus] = useState<QuickAuthStatus>('loading');
|
const [status, setStatus] = useState<QuickAuthStatus>('loading');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates a QuickAuth token with the server-side API
|
* Validates a QuickAuth token with the server-side API
|
||||||
*
|
*
|
||||||
* @param {string} authToken - The JWT token to validate
|
* @param {string} authToken - The JWT token to validate
|
||||||
* @returns {Promise<AuthenticatedUser | null>} User data if valid, null otherwise
|
* @returns {Promise<AuthenticatedUser | null>} User data if valid, null otherwise
|
||||||
*/
|
*/
|
||||||
const validateTokenWithServer = async (authToken: string): Promise<AuthenticatedUser | null> => {
|
const validateTokenWithServer = async (
|
||||||
|
authToken: string,
|
||||||
|
): Promise<AuthenticatedUser | null> => {
|
||||||
try {
|
try {
|
||||||
const validationResponse = await fetch('/api/auth/validate', {
|
const validationResponse = await fetch('/api/auth/validate', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -85,7 +88,7 @@ export function useQuickAuth(): UseQuickAuthReturn {
|
|||||||
const responseData = await validationResponse.json();
|
const responseData = await validationResponse.json();
|
||||||
return responseData.user;
|
return responseData.user;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Token validation failed:', error);
|
console.error('Token validation failed:', error);
|
||||||
@ -102,11 +105,11 @@ export function useQuickAuth(): UseQuickAuthReturn {
|
|||||||
try {
|
try {
|
||||||
// Attempt to retrieve existing token from QuickAuth SDK
|
// Attempt to retrieve existing token from QuickAuth SDK
|
||||||
const { token } = await sdk.quickAuth.getToken();
|
const { token } = await sdk.quickAuth.getToken();
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
// Validate the token with our server-side API
|
// Validate the token with our server-side API
|
||||||
const validatedUserSession = await validateTokenWithServer(token);
|
const validatedUserSession = await validateTokenWithServer(token);
|
||||||
|
|
||||||
if (validatedUserSession) {
|
if (validatedUserSession) {
|
||||||
// Token is valid, set authenticated state
|
// Token is valid, set authenticated state
|
||||||
setAuthenticatedUser(validatedUserSession);
|
setAuthenticatedUser(validatedUserSession);
|
||||||
@ -130,24 +133,24 @@ export function useQuickAuth(): UseQuickAuthReturn {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiates the QuickAuth sign-in process
|
* Initiates the QuickAuth sign-in process
|
||||||
*
|
*
|
||||||
* Uses sdk.quickAuth.getToken() to get a QuickAuth session token.
|
* Uses sdk.quickAuth.getToken() to get a QuickAuth session token.
|
||||||
* If there is already a session token in memory that hasn't expired,
|
* If there is already a session token in memory that hasn't expired,
|
||||||
* it will be immediately returned, otherwise a fresh one will be acquired.
|
* it will be immediately returned, otherwise a fresh one will be acquired.
|
||||||
*
|
*
|
||||||
* @returns {Promise<boolean>} True if sign-in was successful, false otherwise
|
* @returns {Promise<boolean>} True if sign-in was successful, false otherwise
|
||||||
*/
|
*/
|
||||||
const signIn = useCallback(async (): Promise<boolean> => {
|
const signIn = useCallback(async (): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
setStatus('loading');
|
setStatus('loading');
|
||||||
|
|
||||||
// Get QuickAuth session token
|
// Get QuickAuth session token
|
||||||
const { token } = await sdk.quickAuth.getToken();
|
const { token } = await sdk.quickAuth.getToken();
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
// Validate the token with our server-side API
|
// Validate the token with our server-side API
|
||||||
const validatedUserSession = await validateTokenWithServer(token);
|
const validatedUserSession = await validateTokenWithServer(token);
|
||||||
|
|
||||||
if (validatedUserSession) {
|
if (validatedUserSession) {
|
||||||
// Authentication successful, update user state
|
// Authentication successful, update user state
|
||||||
setAuthenticatedUser(validatedUserSession);
|
setAuthenticatedUser(validatedUserSession);
|
||||||
@ -155,7 +158,7 @@ export function useQuickAuth(): UseQuickAuthReturn {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authentication failed, clear user state
|
// Authentication failed, clear user state
|
||||||
setStatus('unauthenticated');
|
setStatus('unauthenticated');
|
||||||
return false;
|
return false;
|
||||||
@ -168,7 +171,7 @@ export function useQuickAuth(): UseQuickAuthReturn {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs out the current user and clears the authentication state
|
* Signs out the current user and clears the authentication state
|
||||||
*
|
*
|
||||||
* Since QuickAuth tokens are managed in memory only, this simply clears
|
* Since QuickAuth tokens are managed in memory only, this simply clears
|
||||||
* the local user state. The actual token will be cleared when the
|
* the local user state. The actual token will be cleared when the
|
||||||
* user signs out of their Farcaster client.
|
* user signs out of their Farcaster client.
|
||||||
@ -181,7 +184,7 @@ export function useQuickAuth(): UseQuickAuthReturn {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the current authentication token from QuickAuth
|
* Retrieves the current authentication token from QuickAuth
|
||||||
*
|
*
|
||||||
* @returns {Promise<string | null>} The current auth token, or null if not authenticated
|
* @returns {Promise<string | null>} The current auth token, or null if not authenticated
|
||||||
*/
|
*/
|
||||||
const getToken = useCallback(async (): Promise<string | null> => {
|
const getToken = useCallback(async (): Promise<string | null> => {
|
||||||
@ -201,4 +204,4 @@ export function useQuickAuth(): UseQuickAuthReturn {
|
|||||||
signOut,
|
signOut,
|
||||||
getToken,
|
getToken,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user