sol-mem-gen/src/app/api/flux/route.ts
2025-02-06 11:18:22 +05:30

126 lines
3.7 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import BN from 'bn.js';
import { fal } from "@fal-ai/client"
import { FLUX_MODELS } from '../../../services/fluxService'
import { verifyPayment, markSignatureAsUsed } from '../../../utils/verifyPayment';
import { uploadToPinata } from '../../../utils/uploadToPinata';
if (!process.env.FAL_AI_KEY) {
throw new Error('FAL_AI_KEY is not configured in environment variables')
}
// Configure fal client
fal.config({
credentials: process.env.FAL_AI_KEY
})
// Consistent image size for all generations
const IMAGE_WIDTH: number = 1024
const IMAGE_HEIGHT: number = 1024
export async function POST(req: NextRequest): Promise<NextResponse> {
try {
const { prompt, modelId, transactionSignature } = await req.json();
const host = req.headers.get("host"); // Get the hostname from request headers
const protocol = req.headers.get("x-forwarded-proto") || "http"; // Handle reverse proxies
if (!prompt || !modelId) {
return NextResponse.json(
{ error: 'Prompt and modelId are required' },
{ status: 400 }
)
}
if (!transactionSignature) {
return NextResponse.json(
{ error: 'Transaction signature is required' },
{ status: 400 }
)
}
const model = FLUX_MODELS.find((model) => model.modelId === modelId)
if (!model) {
return NextResponse.json(
{ error: 'Invalid modelId' },
{ status: 400 }
)
}
const amountOfMTM: BN[] = (global as any).quotesService.getMTMAmountsFor1USDC();
const lowestAmountOfMTM = amountOfMTM.reduce((minQuote, currentQuote) => BN.min(minQuote, currentQuote), amountOfMTM[0]);
const scale = new BN(100);
const scaledModelCost = new BN(model.cost).mul(scale);
const lowestTokenAmountForModel = scaledModelCost.mul(new BN(lowestAmountOfMTM)).div(scale);
const isPaymentVerified = await verifyPayment(transactionSignature, lowestTokenAmountForModel);
if (!isPaymentVerified) {
return NextResponse.json(
{ error: 'Payment verification failed or transaction signature has already been used' },
{ status: 400 }
)
}
await markSignatureAsUsed(transactionSignature);
console.log('Generating with Flux model:', modelId)
console.log('Prompt:', prompt)
const result = await fal.subscribe(modelId, {
input: {
prompt: prompt,
image_size: {
width: IMAGE_WIDTH,
height: IMAGE_HEIGHT
},
},
logs: true,
onQueueUpdate: (update) => {
if (update.status === "IN_PROGRESS") {
console.log('Generation progress:', update.logs.map((log) => log.message))
}
},
})
console.log('Flux generation result:', result)
// Extract the image URL from the response
const imageUrl = result.data?.images?.[0]?.url
console.log(imageUrl);
if (!imageUrl) {
console.error('No image URL in response:', result)
return NextResponse.json(
{ error: 'No image URL in response: ', result },
{ status: 400 }
)
}
const pinataResult = await uploadToPinata(imageUrl);
if (pinataResult.error) {
return NextResponse.json(
{ error: 'Failed to upload image to Pinata' },
{ status: 500 }
)
}
// Extract CID from the URL
const cid = pinataResult.imageUrl!.split("/ipfs/")[1];
const publicUrl = `${protocol}://${host}/api/image/${cid}`;
return NextResponse.json({ imageUrl: publicUrl })
} catch (error) {
console.error('Flux generation error:', error)
return NextResponse.json(
{ error: 'Failed to generate image' },
{ status: 500 }
)
}
}
export const dynamic = 'force-dynamic'