diff --git a/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx b/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx index b5575409..8ed712a6 100644 --- a/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx +++ b/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx @@ -37,7 +37,7 @@ import { PopoverTrigger, } from "@/components/ui/popover"; import ToolTip from "@/components/ToolTip"; -import { BASE_URL } from "@/utils/constant"; +import { getEnv } from "@/utils/constant"; interface ImageEditorProps { initialImage: string | null; @@ -310,6 +310,8 @@ const ImageEditor = ({ if (!src) return ""; return src.startsWith("user") ? `file://${src}` : `file://${src}`; }; + const urls = getEnv(); + const BASE_URL = urls.BASE_URL; return ( <> diff --git a/servers/nextjs/app/(presentation-generator)/presentation/components/Header.tsx b/servers/nextjs/app/(presentation-generator)/presentation/components/Header.tsx index b35e886e..165f327c 100644 --- a/servers/nextjs/app/(presentation-generator)/presentation/components/Header.tsx +++ b/servers/nextjs/app/(presentation-generator)/presentation/components/Header.tsx @@ -129,23 +129,28 @@ const Header = ({ const getSlideMetadata = async () => { try { + // Get the current URL without any query parameters + const baseUrl = window.location.origin + window.location.pathname; + const urls = getEnv(); + const response = await fetch( `${urls.NEXT_PUBLIC_URL}/api/slide-metadata`, { method: "POST", headers: getHeader(), body: JSON.stringify({ - url: window.location.href, + url: baseUrl, theme: currentTheme, customColors: currentColors, tempDirectory: urls.TEMP_DIRECTORY, + baseUrl: urls.BASE_URL, }), } ); - if (!response.ok) { - throw new Error("Failed to fetch metadata"); + const errorData = await response.json(); + throw new Error(errorData.error || "Failed to fetch metadata"); } const metadata = await response.json(); console.log("metadata", metadata); @@ -153,7 +158,11 @@ const Header = ({ } catch (error) { setShowLoader(false); console.error("Error fetching metadata:", error); - // You might want to show an error toast/notification here + toast({ + title: "Error fetching slide metadata", + description: error instanceof Error ? error.message : "Failed to fetch metadata", + variant: "destructive", + }); throw error; } }; diff --git a/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx b/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx index a3afad7c..9382e25d 100644 --- a/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx +++ b/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx @@ -54,6 +54,8 @@ function useDebounce void>( } const PresentationPage = ({ presentation_id }: { presentation_id: string }) => { + const urls = getEnv(); + const BASE_URL = urls.BASE_URL; const dispatch = useDispatch(); const [loading, setLoading] = useState(true); const [selectedSlide, setSelectedSlide] = useState(0); diff --git a/servers/nextjs/app/api/read-config/route.ts b/servers/nextjs/app/api/read-config/route.ts new file mode 100644 index 00000000..68a12481 --- /dev/null +++ b/servers/nextjs/app/api/read-config/route.ts @@ -0,0 +1,30 @@ +import { NextRequest, NextResponse } from "next/server"; +import fs from "fs"; + +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url); + const path = searchParams.get('path'); + + if (!path) { + return NextResponse.json( + { error: "Path parameter is required" }, + { status: 400 } + ); + } + + let config = {}; + if (fs.existsSync(path)) { + const configData = fs.readFileSync(path, 'utf-8'); + config = JSON.parse(configData); + } + + return NextResponse.json({ config }); + } catch (error) { + console.error("Error reading config:", error); + return NextResponse.json( + { error: "Failed to read configuration" }, + { status: 500 } + ); + } +} \ No newline at end of file diff --git a/servers/nextjs/app/api/save-user-config/route.ts b/servers/nextjs/app/api/save-user-config/route.ts new file mode 100644 index 00000000..5824fab2 --- /dev/null +++ b/servers/nextjs/app/api/save-user-config/route.ts @@ -0,0 +1,51 @@ +import { NextRequest, NextResponse } from "next/server"; +import fs from "fs"; + +export async function POST(request: NextRequest) { + try { + const body = await request.json(); + const { provider, apiKey, userConfigPath } = body; + + if (!userConfigPath) { + return NextResponse.json( + { error: "User config path not found" }, + { status: 500 } + ); + } + + // Create config object based on provider + const config = { + LLM: provider === "google" ? "gemini-pro" : "gpt-4", + OPENAI_API_KEY: provider === "openai" ? apiKey : undefined, + GOOGLE_API_KEY: provider === "google" ? apiKey : undefined, + }; + + // Read existing config if it exists + let existingConfig = {}; + if (fs.existsSync(userConfigPath)) { + try { + const existingData = fs.readFileSync(userConfigPath, 'utf-8'); + existingConfig = JSON.parse(existingData); + } catch (error) { + console.error("Error reading existing config:", error); + } + } + + // Merge with existing config + const mergedConfig = { + ...existingConfig, + ...config, + }; + + // Write to file + fs.writeFileSync(userConfigPath, JSON.stringify(mergedConfig, null, 2)); + + return NextResponse.json({ success: true, config: mergedConfig }); + } catch (error) { + console.error("Error saving user config:", error); + return NextResponse.json( + { error: "Failed to save configuration" }, + { status: 500 } + ); + } +} diff --git a/servers/nextjs/app/api/slide-metadata/route.ts b/servers/nextjs/app/api/slide-metadata/route.ts index 88d39cb0..dfd89c33 100644 --- a/servers/nextjs/app/api/slide-metadata/route.ts +++ b/servers/nextjs/app/api/slide-metadata/route.ts @@ -102,7 +102,7 @@ export async function POST(request: NextRequest) { let browser; try { const body = await request.json(); - const { url, theme, customColors,tempDirectory } = body; + const { url, theme, customColors, tempDirectory, baseUrl } = body; if (!url) { return NextResponse.json({ error: "Missing URL" }, { status: 400 }); @@ -116,21 +116,31 @@ export async function POST(request: NextRequest) { const page = await browser.newPage(); await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 }); + // Inject the environment variables before loading the page + await page.evaluateOnNewDocument(` + window.env = { + NEXT_PUBLIC_FAST_API: "${baseUrl || 'http://localhost:8000'}", + NEXT_PUBLIC_URL: "${url}", + }; + `); + try { await page.goto(url, { waitUntil: "networkidle0", timeout: 60000, }); } catch (error) { + console.error("Navigation error:", error); await browser.close(); return NextResponse.json({ error: "Failed to Navigate to provided URL" }, { status: 500 }); } + + try { await page.waitForSelector('[data-element-type="slide-container"]', { - timeout: 60000, + timeout: 80000, }); - await page.evaluate( async (params: ThemeParams) => { const { theme, customColors } = params; diff --git a/servers/nextjs/app/dashboard/api/dashboard.ts b/servers/nextjs/app/dashboard/api/dashboard.ts index 0e70a883..e8cf4f64 100644 --- a/servers/nextjs/app/dashboard/api/dashboard.ts +++ b/servers/nextjs/app/dashboard/api/dashboard.ts @@ -24,7 +24,6 @@ export interface PresentationResponse { } export class DashboardApi { - // static BASE_URL = "http://localhost:48388"; static async getPresentations(): Promise { try { @@ -32,7 +31,6 @@ export class DashboardApi { `${BASE_URL}/ppt/user_presentations`, { method: "GET", - headers: getHeader(), } ); if (response.status === 200) { @@ -54,7 +52,7 @@ export class DashboardApi { `${BASE_URL}/ppt/presentation?presentation_id=${id}`, { method: "GET", - headers: getHeader(), + } ); if (response.status === 200) { diff --git a/servers/nextjs/app/page.tsx b/servers/nextjs/app/page.tsx index 3f6e1d79..30cc3dd6 100644 --- a/servers/nextjs/app/page.tsx +++ b/servers/nextjs/app/page.tsx @@ -4,6 +4,7 @@ import { useRouter } from "next/navigation"; import { toast } from "@/hooks/use-toast"; import { Info, ExternalLink, PlayCircle } from "lucide-react"; import Link from "next/link"; +import { getEnv } from "@/utils/constant"; interface ModelOption { value: string; @@ -88,7 +89,9 @@ interface ConfigState { imageModel: string; } + export default function Home() { + const urls = getEnv(); const router = useRouter(); const [config, setConfig] = useState({ provider: "openai", @@ -117,8 +120,7 @@ export default function Home() { }; const currentProvider = PROVIDER_CONFIGS[config.provider]; - const handleSaveConfig = () => { - console.log("cllee"); + const handleSaveConfig = async () => { if (!config.apiKey) { toast({ title: "Error", @@ -126,11 +128,47 @@ export default function Home() { }); return; } - toast({ - title: "Configuration saved", - description: "You can now upload your presentation", - }); - router.push("/upload"); + + try { + const userConfigPath = urls.USER_CONFIG_PATH; + + if (!userConfigPath) { + toast({ + title: "Error", + description: "Configuration path not found", + }); + return; + } + + const response = await fetch('/api/save-user-config', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + provider: config.provider, + apiKey: config.apiKey, + userConfigPath, + }), + }); + + if (!response.ok) { + throw new Error('Failed to save configuration'); + } + + toast({ + title: "Configuration saved", + description: "You can now upload your presentation", + }); + + router.push("/upload"); + } catch (error) { + console.error('Error saving configuration:', error); + toast({ + title: "Error", + description: "Failed to save configuration", + }); + } }; return ( @@ -158,19 +196,17 @@ export default function Home() { - -

- Required for using {config.provider} services -

+ {/* OpenAI Configuration */} +
+ +
+ setConfig(prev => ({ ...prev, OPENAI_API_KEY: e.target.value }))} + className="flex-1 px-4 py-2.5 border border-gray-300 outline-none rounded-lg focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-colors" + placeholder="Enter your OpenAI API key" + /> +
- ))} +

Required for using OpenAI services

+
+ + {/* Google Configuration */} +
+ +
+ setConfig(prev => ({ ...prev, GOOGLE_API_KEY: e.target.value }))} + className="flex-1 px-4 py-2.5 border border-gray-300 outline-none rounded-lg focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-colors" + placeholder="Enter your Google API key" + /> + +
+

Required for using Google services

+