返回 presentation-ai
generate-slide-image.ts
根目录 / src / app / _actions / presentation / generate-slide-image.ts
1 "use server";
2
3 import { utapi } from "@/app/api/uploadthing/core";
4 import { env } from "@/env";
5 import { requireOptionalIntegration } from "@/lib/env/optional-integrations";
6 import { auth } from "@/server/auth";
7 import { db } from "@/server/db";
8 import { fal } from "@fal-ai/client";
9 import { UTFile } from "uploadthing/server";
10
11 // Nano Banana Pro model for presentation slide images
12 // const SLIDE_IMAGE_MODEL = "fal-ai/nano-banana-pro";
13 const DEFAULT_SLIDE_IMAGE_MODEL = "fal-ai/flux-2/flash";
14
15 export async function generateSlideImageAction(
16 prompt: string,
17 imageModel: string = DEFAULT_SLIDE_IMAGE_MODEL,
18 ) {
19 const session = await auth();
20
21 if (!session?.user?.id) {
22 return {
23 success: false,
24 error: "You must be logged in to generate images",
25 };
26 }
27
28 // Admin only feature
29 if (!session.user.isAdmin) {
30 return {
31 success: false,
32 error: "This feature is only available for admin users",
33 };
34 }
35
36 try {
37 const falConfig = requireOptionalIntegration({
38 integration: "FAL",
39 envVar: "FAL_API_KEY",
40 value: env.FAL_API_KEY,
41 feature: "slide image generation",
42 });
43
44 if (!falConfig.ok) {
45 return {
46 success: false,
47 error: falConfig.error,
48 };
49 }
50
51 fal.config({
52 credentials: falConfig.value,
53 });
54
55 console.log(`Generating slide image with model: ${imageModel}`);
56
57 const result = await fal.subscribe(imageModel, {
58 input: {
59 prompt: prompt,
60 num_images: 1,
61 aspect_ratio: "16:9",
62 },
63 });
64
65 const imageUrl = result.data?.images?.[0]?.url;
66
67 if (!imageUrl) {
68 console.log("Failed to generate slide image", result);
69 throw new Error("Failed to generate slide image");
70 }
71
72 console.log(`Generated slide image URL: ${imageUrl}`);
73
74 // Download the image from fal.ai URL
75 const imageResponse = await fetch(imageUrl);
76 if (!imageResponse.ok) {
77 throw new Error("Failed to download image from fal.ai");
78 }
79
80 const imageBlob = await imageResponse.blob();
81 const imageBuffer = await imageBlob.arrayBuffer();
82
83 // Generate a filename
84 const filename = `slide_${Date.now()}.png`;
85
86 // Create a UTFile from the downloaded image
87 const utFile = new UTFile([new Uint8Array(imageBuffer)], filename);
88
89 // Upload to UploadThing
90 const uploadResult = await utapi.uploadFiles([utFile]);
91
92 if (!uploadResult[0]?.data?.ufsUrl) {
93 console.error("Upload error:", uploadResult[0]?.error);
94 throw new Error("Failed to upload image to UploadThing");
95 }
96
97 const permanentUrl = uploadResult[0].data.ufsUrl;
98 console.log(`Uploaded slide image to: ${permanentUrl}`);
99
100 // Store in database
101 const generatedImage = await db.generatedImage.create({
102 data: {
103 url: permanentUrl,
104 prompt: prompt,
105 userId: session.user.id,
106 },
107 });
108
109 return {
110 success: true,
111 image: generatedImage,
112 };
113 } catch (error) {
114 console.error("Error generating slide image:", error);
115 return {
116 success: false,
117 error:
118 error instanceof Error
119 ? error.message
120 : "Failed to generate slide image",
121 };
122 }
123 }
124
124 lines TYPESCRIPT