diff --git a/apps/backend/index.ts b/apps/backend/index.ts index 0ddf71d..22e4cae 100644 --- a/apps/backend/index.ts +++ b/apps/backend/index.ts @@ -14,6 +14,7 @@ import dotenv from "dotenv"; import paymentRoutes from "./routes/payment.routes"; import { router as webhookRouter } from "./routes/webhook.routes"; +import { router as imageRouter } from "./routes/getimage.routes"; const IMAGE_GEN_CREDITS = 1; const TRAIN_MODEL_CREDITS = 20; @@ -155,6 +156,7 @@ app.post("/ai/generate", authMiddleware, async (req, res) => { res.json({ imageId: data.id, + falReqId: request_id }); }); @@ -443,6 +445,7 @@ app.get("/model/status/:modelId", authMiddleware, async (req, res) => { app.use("/payment", paymentRoutes); app.use("/api/webhook", webhookRouter); +app.use("/api/image/", imageRouter); app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); diff --git a/apps/backend/routes/getimage.routes.ts b/apps/backend/routes/getimage.routes.ts new file mode 100644 index 0000000..16ece50 --- /dev/null +++ b/apps/backend/routes/getimage.routes.ts @@ -0,0 +1,24 @@ +import { prismaClient } from "db"; +import { Router } from "express" + +export const router = Router() + +router.post('/status/:falreqid', async (req, res) => { + const falReqId = req.params.falreqid; + try { + const image = await prismaClient.outputImages.findFirst({ + where: { + AND: [{ falAiRequestId: falReqId }, { userId: req.userId }] + }, + select: { + imageUrl: true, + status: true + } + }) + + res.json({ imageUrl: image?.imageUrl, status: image?.status }) + } catch (error) { + console.error(error) + res.status(403).json({ msg: "Error while fetching image" }) + } +}) \ No newline at end of file diff --git a/apps/web/components/GenerateImage.tsx b/apps/web/components/GenerateImage.tsx index 7343f06..9fb2501 100644 --- a/apps/web/components/GenerateImage.tsx +++ b/apps/web/components/GenerateImage.tsx @@ -1,6 +1,6 @@ "use client" import { useAuth } from "@clerk/nextjs" -import { useState } from "react" +import { useEffect, useState } from "react" import { Button } from "./ui/button" import { Textarea } from "@/components/ui/textarea" import axios from "axios" @@ -11,10 +11,18 @@ import { motion } from "framer-motion" import { Sparkles } from "lucide-react" import { useCredits } from "@/hooks/use-credits" import { useRouter } from "next/navigation" +import { Badge } from "./ui/badge" +import { Dialog, DialogTrigger } from "./ui/dialog" +import Image from "next/image" +import { Skeleton } from "./ui/skeleton" export function GenerateImage() { const [prompt, setPrompt] = useState("") const [selectedModel, setSelectedModel] = useState() const [isGenerating, setIsGenerating] = useState(false) + const [falReqId, setFalReqId] = useState("e05658a5-4904-49fd-bc4d-bf67d32511fa") + const [imageStatus, setImageStatus] = useState<"Pending" | "Generated" | "Failed">("Pending") + const [imageUrl, setImageUrl] = useState() + const [, setImageLoading] = useState(true) const { getToken } = useAuth() const { credits } = useCredits(); const router = useRouter() @@ -26,11 +34,11 @@ export function GenerateImage() { router.push("/pricing") return } - + setIsGenerating(true) try { const token = await getToken() - await axios.post(`${BACKEND_URL}/ai/generate`, { + const response = await axios.post(`${BACKEND_URL}/ai/generate`, { prompt, modelId: selectedModel, num: 1 @@ -39,6 +47,7 @@ export function GenerateImage() { }) toast.success("Image generation started!") setPrompt("") + setFalReqId(response.data.falReqId) } catch (error) { toast.error("Failed to generate image") } finally { @@ -46,15 +55,44 @@ export function GenerateImage() { } } + async function fetchImage() { + try { + setImageStatus('Pending') + const res = await axios.post(`${BACKEND_URL}/api/image/status/${falReqId}`) + if (res.data.status == 'Failed') { + setImageLoading(false) + setImageStatus('Failed') + return + } + setImageStatus(res.data.status) + if (res.data.imageUrl) setImageUrl(res.data.imageUrl) + } catch (error) { + setImageStatus('Failed') + setImageLoading(false) + console.error(error) + } + } + + useEffect(() => { + if (!falReqId || imageUrl) return; + + const interval = setInterval(() => { + fetchImage(); + }, 2000); + + return () => clearInterval(interval); + + }, [falReqId, imageUrl]) + return ( - -
+
-
+ { + isGenerating && +
+
+
+

Result

+ {imageStatus} +
+ + + { + !imageUrl ? + : + image + } + + +
+

Your request will cost 1 credit per operation.

+
+
+
+ } ) } \ No newline at end of file