From f200c84d0cb1463cc9e049ffa08c9e10e01520f4 Mon Sep 17 00:00:00 2001 From: sudipnext Date: Sun, 29 Mar 2026 17:11:22 +0545 Subject: [PATCH 1/3] feat: Add support for dynamic FastAPI origin in image asset handling --- .../servers/fastapi/models/sql/image_asset.py | 23 ++++++++++++++++--- electron/servers/fastapi/server.py | 7 +++++- .../services/image_generation_service.py | 20 +++++++++++++--- electron/servers/fastapi/utils/get_env.py | 4 ++++ 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/electron/servers/fastapi/models/sql/image_asset.py b/electron/servers/fastapi/models/sql/image_asset.py index 76303ec3..8639f6ce 100644 --- a/electron/servers/fastapi/models/sql/image_asset.py +++ b/electron/servers/fastapi/models/sql/image_asset.py @@ -7,10 +7,23 @@ from sqlalchemy import JSON, Column, DateTime from sqlmodel import Field, SQLModel from utils.datetime_utils import get_current_utc_datetime -from utils.get_env import get_app_data_directory_env +from utils.get_env import get_app_data_directory_env, get_next_public_fast_api_env from utils.path_helpers import get_resource_path +def _with_fastapi_origin(path: str) -> str: + """Prefix relative web paths with FastAPI origin when available.""" + if path.startswith("http://") or path.startswith("https://"): + return path + + fastapi_origin = (get_next_public_fast_api_env() or "").strip() + if not fastapi_origin: + return path + + normalized_path = path if path.startswith("/") else f"/{path}" + return f"{fastapi_origin.rstrip('/')}{normalized_path}" + + class ImageAsset(SQLModel, table=True): id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True) created_at: datetime = Field( @@ -36,6 +49,10 @@ class ImageAsset(SQLModel, table=True): if path.startswith("http://") or path.startswith("https://"): return path + # Already a web path under known mounts + if path.startswith("/app_data/") or path.startswith("/static/"): + return _with_fastapi_origin(path) + # Normalize filesystem path real_path = os.path.realpath(path) @@ -46,7 +63,7 @@ class ImageAsset(SQLModel, table=True): if real_path.startswith(app_data_dir_real): rel = os.path.relpath(real_path, app_data_dir_real) rel_web = rel.replace(os.sep, "/") - return f"/app_data/{rel_web}" + return _with_fastapi_origin(f"/app_data/{rel_web}") # Map packaged static assets to /static/... static_root = get_resource_path("static") @@ -54,7 +71,7 @@ class ImageAsset(SQLModel, table=True): if real_path.startswith(static_root_real): rel = os.path.relpath(real_path, static_root_real) rel_web = rel.replace(os.sep, "/") - return f"/static/{rel_web}" + return _with_fastapi_origin(f"/static/{rel_web}") # Fallback: return the original path (may be absolute or relative); # frontend can decide how to handle unusual cases. diff --git a/electron/servers/fastapi/server.py b/electron/servers/fastapi/server.py index 0e1f3994..3c4abca5 100644 --- a/electron/servers/fastapi/server.py +++ b/electron/servers/fastapi/server.py @@ -1,5 +1,6 @@ import uvicorn import argparse +import os from api.main import app if __name__ == "__main__": @@ -12,10 +13,14 @@ if __name__ == "__main__": ) args = parser.parse_args() reload = args.reload == "true" + host = "127.0.0.1" + + # Provide a predictable public URL for services that need absolute asset links. + os.environ.setdefault("FASTAPI_PUBLIC_URL", f"http://{host}:{args.port}") uvicorn.run( "api.main:app", - host="127.0.0.1", + host=host, port=args.port, log_level="info", reload=reload, diff --git a/electron/servers/fastapi/services/image_generation_service.py b/electron/servers/fastapi/services/image_generation_service.py index adbf881a..8e14b8be 100644 --- a/electron/servers/fastapi/services/image_generation_service.py +++ b/electron/servers/fastapi/services/image_generation_service.py @@ -12,6 +12,7 @@ from models.sql.image_asset import ImageAsset from utils.get_env import ( get_dall_e_3_quality_env, get_gpt_image_1_5_quality_env, + get_next_public_fast_api_env, get_pexels_api_key_env, ) from utils.get_env import get_pixabay_api_key_env @@ -59,6 +60,17 @@ class ImageGenerationService: def is_stock_provider_selected(self): return is_pixels_selected() or is_pixabay_selected() + def _to_frontend_url(self, path: str) -> str: + if path.startswith("http://") or path.startswith("https://"): + return path + + fastapi_origin = (get_next_public_fast_api_env() or "").strip() + if not fastapi_origin: + return path + + normalized_path = path if path.startswith("/") else f"/{path}" + return f"{fastapi_origin.rstrip('/')}{normalized_path}" + async def generate_image(self, prompt: ImagePrompt) -> str | ImageAsset: """ Generates an image based on the provided prompt. @@ -69,11 +81,11 @@ class ImageGenerationService: """ if self.is_image_generation_disabled: print("Image generation is disabled. Using placeholder image.") - return "/static/images/placeholder.jpg" + return self._to_frontend_url("/static/images/placeholder.jpg") if not self.image_gen_func: print("No image generation function found. Using placeholder image.") - return "/static/images/placeholder.jpg" + return self._to_frontend_url("/static/images/placeholder.jpg") image_prompt = prompt.get_image_prompt( with_theme=not self.is_stock_provider_selected() @@ -99,11 +111,13 @@ class ImageGenerationService: "theme_prompt": prompt.theme_prompt, }, ) + elif image_path.startswith("/app_data/") or image_path.startswith("/static/"): + return self._to_frontend_url(image_path) raise Exception(f"Image not found at {image_path}") except Exception as e: print(f"Error generating image: {e}") - return "/static/images/placeholder.jpg" + return self._to_frontend_url("/static/images/placeholder.jpg") async def generate_image_openai( self, prompt: str, output_directory: str, model: str, quality: str diff --git a/electron/servers/fastapi/utils/get_env.py b/electron/servers/fastapi/utils/get_env.py index 74cf2e1f..72766baa 100644 --- a/electron/servers/fastapi/utils/get_env.py +++ b/electron/servers/fastapi/utils/get_env.py @@ -142,3 +142,7 @@ def get_codex_model_env(): def get_migrate_database_on_startup_env(): return os.getenv("MIGRATE_DATABASE_ON_STARTUP") + + +def get_next_public_fast_api_env(): + return os.getenv("NEXT_PUBLIC_FAST_API") From cf74b79c8ea51b906544ed5c0b20fa6352170200 Mon Sep 17 00:00:00 2001 From: sudipnext Date: Sun, 29 Mar 2026 17:11:35 +0545 Subject: [PATCH 2/3] feat: Update environment variable for FastAPI public URL --- electron/servers/fastapi/utils/get_env.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electron/servers/fastapi/utils/get_env.py b/electron/servers/fastapi/utils/get_env.py index 72766baa..53680acd 100644 --- a/electron/servers/fastapi/utils/get_env.py +++ b/electron/servers/fastapi/utils/get_env.py @@ -145,4 +145,4 @@ def get_migrate_database_on_startup_env(): def get_next_public_fast_api_env(): - return os.getenv("NEXT_PUBLIC_FAST_API") + return os.getenv("FASTAPI_PUBLIC_URL") From 6951340a85bdc91ccda541a2815f681059875701 Mon Sep 17 00:00:00 2001 From: shiva raj badu Date: Sun, 29 Mar 2026 17:42:06 +0545 Subject: [PATCH 3/3] fix: slide image rendering issue --- .../(dashboard)/dashboard/components/PresentationCard.tsx | 4 ++-- .../components/PresentationRender.tsx | 6 +++--- .../presentation/components/PresentationPage.tsx | 5 ----- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/electron/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/PresentationCard.tsx b/electron/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/PresentationCard.tsx index 3da715e3..87658cf4 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/PresentationCard.tsx +++ b/electron/servers/nextjs/app/(presentation-generator)/(dashboard)/dashboard/components/PresentationCard.tsx @@ -111,14 +111,14 @@ export const PresentationCard = ({
- +
- +

{new Date(presentation?.created_at).toLocaleDateString()} diff --git a/electron/servers/nextjs/app/(presentation-generator)/components/PresentationRender.tsx b/electron/servers/nextjs/app/(presentation-generator)/components/PresentationRender.tsx index e2781787..be00436d 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/components/PresentationRender.tsx +++ b/electron/servers/nextjs/app/(presentation-generator)/components/PresentationRender.tsx @@ -6,7 +6,7 @@ import { V1ContentRender } from '../../(presentation-generator)/components/V1Con const BASE_WIDTH = 1280; const BASE_HEIGHT = 720; -const SlideScale = ({ slide, theme }: { slide: any, theme?: any }) => { +const SlideScale = ({ slide, theme, isClickable = false }: { slide: any, theme?: any, isClickable?: boolean }) => { const containerRef = useRef(null); const [containerWidth, setContainerWidth] = useState(0); @@ -62,11 +62,11 @@ const SlideScale = ({ slide, theme }: { slide: any, theme?: any }) => { } as React.CSSProperties} > - {/*

diff --git a/electron/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx b/electron/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx index 94795e98..62be03c9 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx +++ b/electron/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx @@ -34,11 +34,6 @@ const PresentationPage: React.FC = ({ const [isFullscreen, setIsFullscreen] = useState(false); const [error, setError] = useState(false); - // Ensure /app_data and /static image paths resolve through FastAPI in Electron. - useEffect(() => { - const observer = setupImageUrlConverter(); - return () => observer?.disconnect(); - }, []); const { presentationData, isStreaming } = useSelector(