diff --git a/servers/fastapi/api/middlewares.py b/servers/fastapi/api/middlewares.py index b8bcd11c..ef01d6bf 100644 --- a/servers/fastapi/api/middlewares.py +++ b/servers/fastapi/api/middlewares.py @@ -1,3 +1,5 @@ +import re + from fastapi import Request from starlette.responses import JSONResponse from starlette.middleware.base import BaseHTTPMiddleware @@ -23,6 +25,12 @@ class SessionAuthMiddleware(BaseHTTPMiddleware): _EXEMPT_PREFIXES = ( "/api/v1/auth/", ) + # PPTX/PDF export loads /pdf-maker in a headless browser with no session cookie; it + # only needs a single-deck read by id. (UUID is not a secret; this matches prior behavior + # when auth middleware did not protect these routes during export.) + _PRESENTATION_GET_BY_ID = re.compile( + r"^/api/v1/ppt/presentation/[0-9a-fA-F-]{36}/?$" + ) _PROTECTED_NON_API_PATHS = { "/docs", "/openapi.json", @@ -32,6 +40,11 @@ class SessionAuthMiddleware(BaseHTTPMiddleware): def _is_exempt(self, path: str) -> bool: return any(path.startswith(prefix) for prefix in self._EXEMPT_PREFIXES) + def _is_presentation_get_by_id(self, request: Request, path: str) -> bool: + if request.method != "GET": + return False + return bool(self._PRESENTATION_GET_BY_ID.match(path)) + def _requires_auth(self, path: str) -> bool: if path.startswith("/api/"): return True @@ -46,6 +59,7 @@ class SessionAuthMiddleware(BaseHTTPMiddleware): request.method == "OPTIONS" or not self._requires_auth(path) or self._is_exempt(path) + or self._is_presentation_get_by_id(request, path) ): return await call_next(request) diff --git a/servers/nextjs/app/(export)/layout.tsx b/servers/nextjs/app/(export)/layout.tsx new file mode 100644 index 00000000..1fd24e10 --- /dev/null +++ b/servers/nextjs/app/(export)/layout.tsx @@ -0,0 +1,10 @@ +import React from "react"; + +/** + * Do not wrap with ConfigurationInitializer: it always mounts with isLoading=true + * and only clears after useEffect, so headless PDF/PPTX export captures the + * "Initializing Application" screen. Export only needs the slide renderer; no LLM check. + */ +export default function Layout({ children }: { children: React.ReactNode }) { + return <>{children}; +} diff --git a/servers/nextjs/app/(presentation-generator)/pdf-maker/PdfMakerPage.tsx b/servers/nextjs/app/(export)/pdf-maker/PdfMakerPage.tsx similarity index 94% rename from servers/nextjs/app/(presentation-generator)/pdf-maker/PdfMakerPage.tsx rename to servers/nextjs/app/(export)/pdf-maker/PdfMakerPage.tsx index cfdf172e..d48501a2 100644 --- a/servers/nextjs/app/(presentation-generator)/pdf-maker/PdfMakerPage.tsx +++ b/servers/nextjs/app/(export)/pdf-maker/PdfMakerPage.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { RootState } from "@/store/store"; -import "../utils/prism-languages"; +import "@/app/(presentation-generator)/utils/prism-languages"; import { Skeleton } from "@/components/ui/skeleton"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; @@ -10,13 +10,12 @@ import { usePathname } from "next/navigation"; import { trackEvent, MixpanelEvent } from "@/utils/mixpanel"; import { AlertCircle } from "lucide-react"; import { setPresentationData } from "@/store/slices/presentationGeneration"; -import { DashboardApi } from "../services/api/dashboard"; +import { DashboardApi } from "@/app/(presentation-generator)/services/api/dashboard"; import { setupImageUrlConverter } from "@/utils/image-url-converter"; - -import { V1ContentRender } from "../components/V1ContentRender"; -import { useFontLoader } from "../hooks/useFontLoad"; -import { Theme } from "../services/api/types"; +import { V1ContentRender } from "@/app/(presentation-generator)/components/V1ContentRender"; +import { useFontLoader } from "@/app/(presentation-generator)/hooks/useFontLoad"; +import { Theme } from "@/app/(presentation-generator)/services/api/types"; diff --git a/servers/nextjs/app/(presentation-generator)/pdf-maker/page.tsx b/servers/nextjs/app/(export)/pdf-maker/page.tsx similarity index 100% rename from servers/nextjs/app/(presentation-generator)/pdf-maker/page.tsx rename to servers/nextjs/app/(export)/pdf-maker/page.tsx diff --git a/servers/nextjs/app/(presentation-generator)/services/api/dashboard.ts b/servers/nextjs/app/(presentation-generator)/services/api/dashboard.ts index 8f287afb..816b0f23 100644 --- a/servers/nextjs/app/(presentation-generator)/services/api/dashboard.ts +++ b/servers/nextjs/app/(presentation-generator)/services/api/dashboard.ts @@ -53,6 +53,7 @@ export class DashboardApi { getApiUrl(`/api/v1/ppt/presentation/${id}`), { method: "GET", + credentials: "include", } ); diff --git a/servers/nextjs/app/ConfigurationInitializer.tsx b/servers/nextjs/app/ConfigurationInitializer.tsx index 9225d699..40da4727 100644 --- a/servers/nextjs/app/ConfigurationInitializer.tsx +++ b/servers/nextjs/app/ConfigurationInitializer.tsx @@ -12,9 +12,11 @@ import { getApiUrl } from '@/utils/api'; export function ConfigurationInitializer({ children }: { children: React.ReactNode }) { const dispatch = useDispatch(); - const [isLoading, setIsLoading] = useState(true); - const router = useRouter(); const route = usePathname(); + const [isLoading, setIsLoading] = useState( + () => !route?.startsWith("/pdf-maker") + ); + const router = useRouter(); // Fetch user config state useEffect(() => { @@ -31,13 +33,13 @@ export function ConfigurationInitializer({ children }: { children: React.ReactNo } const fetchUserConfigState = async () => { - setIsLoading(true); - - if (route.startsWith('/pdf-maker')) { + if (route.startsWith("/pdf-maker")) { setIsLoading(false); return; } + setIsLoading(true); + let canChangeKeys = false; try { const res = await fetch('/api/can-change-keys'); @@ -64,7 +66,6 @@ export function ConfigurationInitializer({ children }: { children: React.ReactNo dispatch(setLLMConfig(llmConfig)); const isValid = hasValidLLMConfig(llmConfig); - console.log('isValid', isValid); if (route.startsWith('/pdf-maker')) { setIsLoading(false); return; diff --git a/servers/nextjs/utils/storeHelpers.ts b/servers/nextjs/utils/storeHelpers.ts index f863b838..bec345ab 100644 --- a/servers/nextjs/utils/storeHelpers.ts +++ b/servers/nextjs/utils/storeHelpers.ts @@ -128,10 +128,5 @@ export const handleSaveLLMConfig = async (llmConfig: LLMConfig) => { store.dispatch(setLLMConfig(llmConfig)); }; -export const hasValidLLMConfig = (llmConfig: LLMConfig) => { - console.log('llmConfig', llmConfig); - - const validationError = getLLMConfigValidationError(llmConfig); - console.log('validationError', validationError); - return validationError === null; -} +export const hasValidLLMConfig = (llmConfig: LLMConfig) => + getLLMConfigValidationError(llmConfig) === null;