mirror of
https://github.com/neynarxyz/create-farcaster-mini-app.git
synced 2025-11-16 08:08:56 -05:00
refactor: move env vars to constants file
This commit is contained in:
parent
40e40543cd
commit
5f0fd8876a
@ -6,7 +6,7 @@ import { dirname } from 'path';
|
|||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { mnemonicToAccount } from 'viem/accounts';
|
import crypto from 'crypto';
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = dirname(__filename);
|
const __dirname = dirname(__filename);
|
||||||
@ -295,6 +295,7 @@ export async function init() {
|
|||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
"eslint-config-next": "15.0.3",
|
"eslint-config-next": "15.0.3",
|
||||||
"localtunnel": "^2.0.2",
|
"localtunnel": "^2.0.2",
|
||||||
|
"pino-pretty": "^13.0.0",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
@ -322,6 +323,7 @@ export async function init() {
|
|||||||
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_NAME="${answers.projectName}"`);
|
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_NAME="${answers.projectName}"`);
|
||||||
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_DESCRIPTION="${answers.description}"`);
|
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_DESCRIPTION="${answers.description}"`);
|
||||||
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_BUTTON_TEXT="${answers.buttonText}"`);
|
fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_BUTTON_TEXT="${answers.buttonText}"`);
|
||||||
|
fs.appendFileSync(envPath, `\nNEXTAUTH_SECRET="${crypto.randomBytes(32).toString('hex')}"`);
|
||||||
if (useNeynar && neynarApiKey && neynarClientId) {
|
if (useNeynar && neynarApiKey && neynarClientId) {
|
||||||
fs.appendFileSync(envPath, `\nNEYNAR_API_KEY="${neynarApiKey}"`);
|
fs.appendFileSync(envPath, `\nNEYNAR_API_KEY="${neynarApiKey}"`);
|
||||||
fs.appendFileSync(envPath, `\nNEYNAR_CLIENT_ID="${neynarClientId}"`);
|
fs.appendFileSync(envPath, `\nNEYNAR_CLIENT_ID="${neynarClientId}"`);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
|
import { APP_NAME } from "~/lib/constants";
|
||||||
|
|
||||||
// note: dynamic import is required for components that use the Frame SDK
|
// note: dynamic import is required for components that use the Frame SDK
|
||||||
const Demo = dynamic(() => import("~/components/Demo"), {
|
const Demo = dynamic(() => import("~/components/Demo"), {
|
||||||
@ -9,7 +9,7 @@ const Demo = dynamic(() => import("~/components/Demo"), {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default function App(
|
export default function App(
|
||||||
{ title }: { title?: string } = { title: process.env.NEXT_PUBLIC_FRAME_NAME || "Frames v2 Demo" }
|
{ title }: { title?: string } = { title: APP_NAME }
|
||||||
) {
|
) {
|
||||||
return <Demo title={title} />;
|
return <Demo title={title} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,10 +3,11 @@ import type { Metadata } from "next";
|
|||||||
import { getSession } from "~/auth"
|
import { getSession } from "~/auth"
|
||||||
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";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: process.env.NEXT_PUBLIC_FRAME_NAME || "Frames v2 Demo",
|
title: APP_NAME,
|
||||||
description: process.env.NEXT_PUBLIC_FRAME_DESCRIPTION || "A Farcaster Frames v2 demo app",
|
description: APP_DESCRIPTION,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function RootLayout({
|
export default async function RootLayout({
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { ImageResponse } from "next/og";
|
import { ImageResponse } from "next/og";
|
||||||
|
import { APP_NAME } from "~/lib/constants";
|
||||||
|
|
||||||
export const alt = process.env.NEXT_PUBLIC_FRAME_NAME || "Frames V2 Demo";
|
export const alt = APP_NAME;
|
||||||
export const size = {
|
export const size = {
|
||||||
width: 600,
|
width: 600,
|
||||||
height: 400,
|
height: 400,
|
||||||
|
|||||||
@ -1,25 +1,19 @@
|
|||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
import App from "./app";
|
import App from "./app";
|
||||||
|
import { APP_URL, APP_NAME, APP_DESCRIPTION, APP_OG_IMAGE_URL, APP_ICON_URL, APP_SPLASH_URL, APP_SPLASH_BACKGROUND_COLOR, APP_BUTTON_TEXT } from "~/lib/constants";
|
||||||
const appUrl = process.env.NEXT_PUBLIC_URL;
|
|
||||||
|
|
||||||
// frame preview metadata
|
|
||||||
const appName = process.env.NEXT_PUBLIC_FRAME_NAME;
|
|
||||||
const splashImageUrl = `${appUrl}/splash.png`;
|
|
||||||
const iconUrl = `${appUrl}/icon.png`;
|
|
||||||
|
|
||||||
const framePreviewMetadata = {
|
const framePreviewMetadata = {
|
||||||
version: "next",
|
version: "next",
|
||||||
imageUrl: `${appUrl}/opengraph-image`,
|
imageUrl: APP_OG_IMAGE_URL,
|
||||||
button: {
|
button: {
|
||||||
title: process.env.NEXT_PUBLIC_FRAME_BUTTON_TEXT,
|
title: APP_BUTTON_TEXT,
|
||||||
action: {
|
action: {
|
||||||
type: "launch_frame",
|
type: "launch_frame",
|
||||||
name: appName,
|
name: APP_NAME,
|
||||||
url: appUrl,
|
url: APP_URL,
|
||||||
splashImageUrl,
|
splashImageUrl: APP_SPLASH_URL,
|
||||||
iconUrl,
|
iconUrl: APP_ICON_URL,
|
||||||
splashBackgroundColor: "#f7f7f7",
|
splashBackgroundColor: APP_SPLASH_BACKGROUND_COLOR,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -28,10 +22,10 @@ export const revalidate = 300;
|
|||||||
|
|
||||||
export async function generateMetadata(): Promise<Metadata> {
|
export async function generateMetadata(): Promise<Metadata> {
|
||||||
return {
|
return {
|
||||||
title: appName,
|
title: APP_NAME,
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: appName,
|
title: APP_NAME,
|
||||||
description: process.env.NEXT_PUBLIC_FRAME_DESCRIPTION,
|
description: APP_DESCRIPTION,
|
||||||
},
|
},
|
||||||
other: {
|
other: {
|
||||||
"fc:frame": JSON.stringify(framePreviewMetadata),
|
"fc:frame": JSON.stringify(framePreviewMetadata),
|
||||||
|
|||||||
8
src/lib/constants.ts
Normal file
8
src/lib/constants.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export const APP_URL = process.env.NEXT_PUBLIC_URL;
|
||||||
|
export const APP_NAME = process.env.NEXT_PUBLIC_FRAME_NAME;
|
||||||
|
export const APP_DESCRIPTION = process.env.NEXT_PUBLIC_FRAME_DESCRIPTION;
|
||||||
|
export const APP_ICON_URL = `${APP_URL}/icon.png`;
|
||||||
|
export const APP_OG_IMAGE_URL = `${APP_URL}/opengraph-image`;
|
||||||
|
export const APP_SPLASH_URL = `${APP_URL}/splash.png`;
|
||||||
|
export const APP_SPLASH_BACKGROUND_COLOR = "#f7f7f7";
|
||||||
|
export const APP_BUTTON_TEXT = process.env.NEXT_PUBLIC_FRAME_BUTTON_TEXT;
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import { FrameNotificationDetails } from "@farcaster/frame-sdk";
|
import { FrameNotificationDetails } from "@farcaster/frame-sdk";
|
||||||
import { Redis } from "@upstash/redis";
|
import { Redis } from "@upstash/redis";
|
||||||
|
import { APP_NAME } from "./constants";
|
||||||
|
|
||||||
// In-memory fallback storage
|
// In-memory fallback storage
|
||||||
const localStore = new Map<string, FrameNotificationDetails>();
|
const localStore = new Map<string, FrameNotificationDetails>();
|
||||||
@ -12,7 +13,7 @@ const redis = useRedis ? new Redis({
|
|||||||
}) : null;
|
}) : null;
|
||||||
|
|
||||||
function getUserNotificationDetailsKey(fid: number): string {
|
function getUserNotificationDetailsKey(fid: number): string {
|
||||||
return `${process.env.NEXT_PUBLIC_FRAME_NAME}:user:${fid}`;
|
return `${APP_NAME}:user:${fid}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUserNotificationDetails(
|
export async function getUserNotificationDetails(
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { NeynarAPIClient, Configuration } from '@neynar/nodejs-sdk';
|
import { NeynarAPIClient, Configuration } from '@neynar/nodejs-sdk';
|
||||||
|
import { APP_URL } from './constants';
|
||||||
|
|
||||||
let neynarClient: NeynarAPIClient | null = null;
|
let neynarClient: NeynarAPIClient | null = null;
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ export async function sendNeynarFrameNotification({
|
|||||||
const notification = {
|
const notification = {
|
||||||
title,
|
title,
|
||||||
body,
|
body,
|
||||||
target_url: process.env.NEXT_PUBLIC_URL!,
|
target_url: APP_URL,
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await client.publishFrameNotifications({
|
const result = await client.publishFrameNotifications({
|
||||||
|
|||||||
@ -3,8 +3,7 @@ import {
|
|||||||
sendNotificationResponseSchema,
|
sendNotificationResponseSchema,
|
||||||
} from "@farcaster/frame-sdk";
|
} from "@farcaster/frame-sdk";
|
||||||
import { getUserNotificationDetails } from "~/lib/kv";
|
import { getUserNotificationDetails } from "~/lib/kv";
|
||||||
|
import { APP_URL } from "./constants";
|
||||||
const appUrl = process.env.NEXT_PUBLIC_URL || "";
|
|
||||||
|
|
||||||
type SendFrameNotificationResult =
|
type SendFrameNotificationResult =
|
||||||
| {
|
| {
|
||||||
@ -38,7 +37,7 @@ export async function sendFrameNotification({
|
|||||||
notificationId: crypto.randomUUID(),
|
notificationId: crypto.randomUUID(),
|
||||||
title,
|
title,
|
||||||
body,
|
body,
|
||||||
targetUrl: appUrl,
|
targetUrl: APP_URL,
|
||||||
tokens: [notificationDetails.token],
|
tokens: [notificationDetails.token],
|
||||||
} satisfies SendNotificationRequest),
|
} satisfies SendNotificationRequest),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import { type ClassValue, clsx } from 'clsx';
|
import { type ClassValue, clsx } from 'clsx';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
import { mnemonicToAccount } from 'viem/accounts';
|
import { mnemonicToAccount } from 'viem/accounts';
|
||||||
|
import { APP_BUTTON_TEXT, APP_ICON_URL, APP_NAME, APP_OG_IMAGE_URL, APP_SPLASH_BACKGROUND_COLOR, APP_URL } from './constants';
|
||||||
|
import { APP_SPLASH_URL } from './constants';
|
||||||
|
|
||||||
interface FrameMetadata {
|
interface FrameMetadata {
|
||||||
accountAssociation?: {
|
accountAssociation?: {
|
||||||
@ -48,13 +50,12 @@ export async function getFarcasterMetadata(): Promise<FrameMetadata> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const appUrl = process.env.NEXT_PUBLIC_URL;
|
if (!APP_URL) {
|
||||||
if (!appUrl) {
|
|
||||||
throw new Error('NEXT_PUBLIC_URL not configured');
|
throw new Error('NEXT_PUBLIC_URL not configured');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the domain from the URL (without https:// prefix)
|
// Get the domain from the URL (without https:// prefix)
|
||||||
const domain = new URL(appUrl).hostname;
|
const domain = new URL(APP_URL).hostname;
|
||||||
console.log('Using domain for manifest:', domain);
|
console.log('Using domain for manifest:', domain);
|
||||||
|
|
||||||
const secretEnvVars = getSecretEnvVars();
|
const secretEnvVars = getSecretEnvVars();
|
||||||
@ -97,19 +98,19 @@ export async function getFarcasterMetadata(): Promise<FrameMetadata> {
|
|||||||
const neynarClientId = process.env.NEYNAR_CLIENT_ID;
|
const neynarClientId = process.env.NEYNAR_CLIENT_ID;
|
||||||
const webhookUrl = neynarApiKey && neynarClientId
|
const webhookUrl = neynarApiKey && neynarClientId
|
||||||
? `https://api.neynar.com/f/app/${neynarClientId}/event`
|
? `https://api.neynar.com/f/app/${neynarClientId}/event`
|
||||||
: `${appUrl}/api/webhook`;
|
: `${APP_URL}/api/webhook`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accountAssociation,
|
accountAssociation,
|
||||||
frame: {
|
frame: {
|
||||||
version: "1",
|
version: "1",
|
||||||
name: process.env.NEXT_PUBLIC_FRAME_NAME || "Frames v2 Demo",
|
name: APP_NAME ?? "Frames v2 Demo",
|
||||||
iconUrl: `${appUrl}/icon.png`,
|
iconUrl: APP_ICON_URL,
|
||||||
homeUrl: appUrl,
|
homeUrl: APP_URL,
|
||||||
imageUrl: `${appUrl}/opengraph-image`,
|
imageUrl: APP_OG_IMAGE_URL,
|
||||||
buttonTitle: process.env.NEXT_PUBLIC_FRAME_BUTTON_TEXT || "Launch Frame",
|
buttonTitle: APP_BUTTON_TEXT ?? "Launch Frame",
|
||||||
splashImageUrl: `${appUrl}/splash.png`,
|
splashImageUrl: APP_SPLASH_URL,
|
||||||
splashBackgroundColor: "#f7f7f7",
|
splashBackgroundColor: APP_SPLASH_BACKGROUND_COLOR,
|
||||||
webhookUrl,
|
webhookUrl,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user