From 4c456526bfc77760f8cfc497d41487cbdb4004d8 Mon Sep 17 00:00:00 2001 From: Jason Wang <40612523+jasonfyw@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:20:33 -0500 Subject: [PATCH 01/14] add: render and cors config --- app/services/api/render.yaml | 36 +++++++++++++++++++ app/services/api/src/finquest_api/config.py | 13 ++++++- .../api/src/finquest_api/routers/users.py | 4 +-- 3 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 app/services/api/render.yaml diff --git a/app/services/api/render.yaml b/app/services/api/render.yaml new file mode 100644 index 0000000..da08f98 --- /dev/null +++ b/app/services/api/render.yaml @@ -0,0 +1,36 @@ +services: + - type: web + name: finquest-api + runtime: python + plan: free + buildCommand: pip install uv && uv sync + startCommand: uv run uvicorn finquest_api.main:app --host 0.0.0.0 --port $PORT + envVars: + # Supabase Configuration - Set these in Render dashboard + - key: SUPABASE_URL + sync: false + - key: SUPABASE_KEY + sync: false + - key: SUPABASE_JWT_SECRET + sync: false + - key: SUPABASE_DB_URL + sync: false + # LLM Configuration - Set these in Render dashboard + - key: LLM_PROVIDER + value: gemini + - key: LLM_BASE_URL + value: https://generativelanguage.googleapis.com/v1beta + - key: LLM_MODEL + value: gemini-2.0-flash + - key: LLM_API_KEY + sync: false + - key: LLM_ORG_ID + sync: false + - key: LLM_TIMEOUT_SECONDS + value: 30.0 + - key: LLM_MAX_RETRIES + value: 2 + # CORS Configuration - Update with your frontend URL + - key: ALLOWED_ORIGINS + value: http://localhost:3000,http://localhost:3001,http://127.0.0.1:3000,https://tryfinquest.vercel.app + diff --git a/app/services/api/src/finquest_api/config.py b/app/services/api/src/finquest_api/config.py index 0f44ad4..5877cca 100644 --- a/app/services/api/src/finquest_api/config.py +++ b/app/services/api/src/finquest_api/config.py @@ -3,7 +3,7 @@ """ from typing import List, Optional -from pydantic import BaseModel, SecretStr +from pydantic import BaseModel, SecretStr, field_validator from pydantic_settings import BaseSettings, SettingsConfigDict @@ -38,6 +38,17 @@ class Settings(BaseSettings): "http://127.0.0.1:3000", ] + @field_validator("ALLOWED_ORIGINS", mode="before") + @classmethod + def parse_cors_origins(cls, v): + """Parse CORS origins from comma-separated string or list""" + if isinstance(v, str): + # Split by comma and strip whitespace, remove empty strings + origins = [origin.strip() for origin in v.split(",") if origin.strip()] + # Remove trailing slashes for consistency + return [origin.rstrip("/") for origin in origins] + return v + # Server Configuration HOST: str = "0.0.0.0" PORT: int = 8000 diff --git a/app/services/api/src/finquest_api/routers/users.py b/app/services/api/src/finquest_api/routers/users.py index 914edb5..8f3f366 100644 --- a/app/services/api/src/finquest_api/routers/users.py +++ b/app/services/api/src/finquest_api/routers/users.py @@ -6,7 +6,7 @@ from sqlalchemy.orm import Session from ..auth_utils import get_current_user from ..db.models import User, OnboardingResponse, Suggestion -from ..db.session import get_session, SessionLocal +from ..db.session import get_session, SessionLocal, get_engine from ..schemas import UpdateProfileRequest, SuggestionResponse from ..services.llm.service import LLMService from ..services.module_generator import ModuleGenerator @@ -26,7 +26,7 @@ async def generate_suggestions_task( user_id: str ): """Background task to generate suggestions""" - db = SessionLocal() + db = SessionLocal(bind=get_engine()) try: user = db.query(User).filter(User.id == user_id).first() if user: From 26095b397ea5c0d99f95e43284502149a05516f3 Mon Sep 17 00:00:00 2001 From: Jason Wang <40612523+jasonfyw@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:21:33 -0500 Subject: [PATCH 02/14] move render config --- app/services/api/render.yaml => render.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/services/api/render.yaml => render.yaml (100%) diff --git a/app/services/api/render.yaml b/render.yaml similarity index 100% rename from app/services/api/render.yaml rename to render.yaml From 9fc8ee2bbe17ff094224b657a6b126c8f61c51b0 Mon Sep 17 00:00:00 2001 From: Jason Wang <40612523+jasonfyw@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:23:50 -0500 Subject: [PATCH 03/14] fix: render config --- render.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/render.yaml b/render.yaml index da08f98..17850ab 100644 --- a/render.yaml +++ b/render.yaml @@ -3,8 +3,8 @@ services: name: finquest-api runtime: python plan: free - buildCommand: pip install uv && uv sync - startCommand: uv run uvicorn finquest_api.main:app --host 0.0.0.0 --port $PORT + buildCommand: cd app/services/api && pip install uv && uv sync + startCommand: cd app/services/api && uv run uvicorn finquest_api.main:app --host 0.0.0.0 --port $PORT envVars: # Supabase Configuration - Set these in Render dashboard - key: SUPABASE_URL From 12f9121c52b60ca1a9796fb8ebe60304d59d66f2 Mon Sep 17 00:00:00 2001 From: Jason Wang <40612523+jasonfyw@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:34:41 -0500 Subject: [PATCH 04/14] add: python version --- render.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/render.yaml b/render.yaml index 17850ab..61b7265 100644 --- a/render.yaml +++ b/render.yaml @@ -6,6 +6,10 @@ services: buildCommand: cd app/services/api && pip install uv && uv sync startCommand: cd app/services/api && uv run uvicorn finquest_api.main:app --host 0.0.0.0 --port $PORT envVars: + - key: PYTHON_VERSION + value: 3.11 + - key: PORT + value: 8000 # Supabase Configuration - Set these in Render dashboard - key: SUPABASE_URL sync: false From 9febd58b82d52e0e8d1cadc95cb0bed8a47809a2 Mon Sep 17 00:00:00 2001 From: Jason Wang <40612523+jasonfyw@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:36:07 -0500 Subject: [PATCH 05/14] fix: python verson --- render.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render.yaml b/render.yaml index 61b7265..09edc1e 100644 --- a/render.yaml +++ b/render.yaml @@ -7,7 +7,7 @@ services: startCommand: cd app/services/api && uv run uvicorn finquest_api.main:app --host 0.0.0.0 --port $PORT envVars: - key: PYTHON_VERSION - value: 3.11 + value: 3.11.12 - key: PORT value: 8000 # Supabase Configuration - Set these in Render dashboard From 37c4f8963684696ec3a6390616d60cb577066ec8 Mon Sep 17 00:00:00 2001 From: Jason Wang <40612523+jasonfyw@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:38:55 -0500 Subject: [PATCH 06/14] fix: build command --- render.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render.yaml b/render.yaml index 09edc1e..0439f68 100644 --- a/render.yaml +++ b/render.yaml @@ -3,7 +3,7 @@ services: name: finquest-api runtime: python plan: free - buildCommand: cd app/services/api && pip install uv && uv sync + buildCommand: cd app/services/api && pip install uv && uv python install 3.11.12 && uv python pin 3.11.12 && uv sync startCommand: cd app/services/api && uv run uvicorn finquest_api.main:app --host 0.0.0.0 --port $PORT envVars: - key: PYTHON_VERSION From 499582ebabae333992da01e1a06a7d8e87ea82e3 Mon Sep 17 00:00:00 2001 From: Jason Wang <40612523+jasonfyw@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:41:23 -0500 Subject: [PATCH 07/14] fix: python install --- render.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/render.yaml b/render.yaml index 0439f68..4e15491 100644 --- a/render.yaml +++ b/render.yaml @@ -3,13 +3,15 @@ services: name: finquest-api runtime: python plan: free - buildCommand: cd app/services/api && pip install uv && uv python install 3.11.12 && uv python pin 3.11.12 && uv sync + buildCommand: cd app/services/api && pip install uv && uv sync --python $(which python3) startCommand: cd app/services/api && uv run uvicorn finquest_api.main:app --host 0.0.0.0 --port $PORT envVars: - key: PYTHON_VERSION value: 3.11.12 - key: PORT value: 8000 + - key: UV_PYTHON_DOWNLOADS + value: manual # Supabase Configuration - Set these in Render dashboard - key: SUPABASE_URL sync: false From dd7c7d8809bdb2ae96b603f6edd83e54a7d5a5ed Mon Sep 17 00:00:00 2001 From: Jason Wang <40612523+jasonfyw@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:46:09 -0500 Subject: [PATCH 08/14] change: python version --- render.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/render.yaml b/render.yaml index 4e15491..e8111e6 100644 --- a/render.yaml +++ b/render.yaml @@ -3,11 +3,11 @@ services: name: finquest-api runtime: python plan: free - buildCommand: cd app/services/api && pip install uv && uv sync --python $(which python3) + buildCommand: cd app/services/api && pip install uv && uv python install 3.9 && uv python pin 3.9 && uv sync startCommand: cd app/services/api && uv run uvicorn finquest_api.main:app --host 0.0.0.0 --port $PORT envVars: - key: PYTHON_VERSION - value: 3.11.12 + value: 3.9.0 - key: PORT value: 8000 - key: UV_PYTHON_DOWNLOADS From 59f97d4d74322c4590ca2ba85c2b4a6252d23b89 Mon Sep 17 00:00:00 2001 From: Jason Wang <40612523+jasonfyw@users.noreply.github.com> Date: Tue, 25 Nov 2025 22:55:19 -0500 Subject: [PATCH 09/14] fix: cors --- app/services/api/src/finquest_api/config.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/services/api/src/finquest_api/config.py b/app/services/api/src/finquest_api/config.py index 5877cca..9644f35 100644 --- a/app/services/api/src/finquest_api/config.py +++ b/app/services/api/src/finquest_api/config.py @@ -36,17 +36,22 @@ class Settings(BaseSettings): "http://localhost:3000", "http://localhost:3001", "http://127.0.0.1:3000", + "https://tryfinquest.vercel.app", ] @field_validator("ALLOWED_ORIGINS", mode="before") @classmethod def parse_cors_origins(cls, v): """Parse CORS origins from comma-separated string or list""" + if v is None: + return [] if isinstance(v, str): # Split by comma and strip whitespace, remove empty strings origins = [origin.strip() for origin in v.split(",") if origin.strip()] # Remove trailing slashes for consistency return [origin.rstrip("/") for origin in origins] + if isinstance(v, list): + return [str(origin).rstrip("/") for origin in v if origin] return v # Server Configuration From ad9eb497ef240690224d7e1440d5b71118c1780b Mon Sep 17 00:00:00 2001 From: Jason Wang <40612523+jasonfyw@users.noreply.github.com> Date: Tue, 25 Nov 2025 23:05:17 -0500 Subject: [PATCH 10/14] fix: allowed origins parsing --- app/services/api/src/finquest_api/config.py | 30 +++++++-------------- app/services/api/src/finquest_api/main.py | 2 +- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/app/services/api/src/finquest_api/config.py b/app/services/api/src/finquest_api/config.py index 9644f35..8978b11 100644 --- a/app/services/api/src/finquest_api/config.py +++ b/app/services/api/src/finquest_api/config.py @@ -31,28 +31,18 @@ class Settings(BaseSettings): API_VERSION: str = "0.1.0" DEBUG: bool = True - # CORS Configuration - ALLOWED_ORIGINS: List[str] = [ - "http://localhost:3000", - "http://localhost:3001", - "http://127.0.0.1:3000", - "https://tryfinquest.vercel.app", - ] + # CORS Configuration - stored as string, converted to list via property + ALLOWED_ORIGINS: str = "http://localhost:3000,http://localhost:3001,http://127.0.0.1:3000,https://tryfinquest.vercel.app" - @field_validator("ALLOWED_ORIGINS", mode="before") - @classmethod - def parse_cors_origins(cls, v): - """Parse CORS origins from comma-separated string or list""" - if v is None: + @property + def allowed_origins_list(self) -> List[str]: + """Parse CORS origins from comma-separated string to list""" + if not self.ALLOWED_ORIGINS: return [] - if isinstance(v, str): - # Split by comma and strip whitespace, remove empty strings - origins = [origin.strip() for origin in v.split(",") if origin.strip()] - # Remove trailing slashes for consistency - return [origin.rstrip("/") for origin in origins] - if isinstance(v, list): - return [str(origin).rstrip("/") for origin in v if origin] - return v + # Split by comma and strip whitespace, remove empty strings + origins = [origin.strip() for origin in self.ALLOWED_ORIGINS.split(",") if origin.strip()] + # Remove trailing slashes for consistency + return [origin.rstrip("/") for origin in origins] # Server Configuration HOST: str = "0.0.0.0" diff --git a/app/services/api/src/finquest_api/main.py b/app/services/api/src/finquest_api/main.py index c3d2403..b9b0f67 100644 --- a/app/services/api/src/finquest_api/main.py +++ b/app/services/api/src/finquest_api/main.py @@ -19,7 +19,7 @@ # Configure CORS app.add_middleware( CORSMiddleware, - allow_origins=settings.ALLOWED_ORIGINS, + allow_origins=settings.allowed_origins_list, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], From 204c7774bc949d8490f73130b181a8753f4ffd89 Mon Sep 17 00:00:00 2001 From: Jason Wang <40612523+jasonfyw@users.noreply.github.com> Date: Tue, 25 Nov 2025 23:32:27 -0500 Subject: [PATCH 11/14] add: separate auth redirects --- app/web/.env.example | 3 ++- app/web/README.md | 31 +++++++++++++++++++++++++++++++ app/web/contexts/AuthContext.tsx | 2 +- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/app/web/.env.example b/app/web/.env.example index a32ea68..4163e4e 100644 --- a/app/web/.env.example +++ b/app/web/.env.example @@ -1,3 +1,4 @@ NEXT_PUBLIC_SUPABASE_URL=url NEXT_PUBLIC_SUPABASE_ANON_KEY=key -NEXT_PUBLIC_API_URL=http://localhost:8000 \ No newline at end of file +NEXT_PUBLIC_API_URL=http://localhost:8000 +NEXT_PUBLIC_SITE_URL=http://localhost:3000 \ No newline at end of file diff --git a/app/web/README.md b/app/web/README.md index 802b4df..af2b1ee 100644 --- a/app/web/README.md +++ b/app/web/README.md @@ -10,6 +10,37 @@ Install dependencies: pnpm install ``` +### Environment Variables + +Create a `.env.local` file in the `app/web/` directory with the following variables: + +```bash +# Supabase Configuration (required) +NEXT_PUBLIC_SUPABASE_URL=your_supabase_url +NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key + +# API Configuration (optional - defaults to http://localhost:8000 for local dev) +NEXT_PUBLIC_API_URL=http://localhost:8000 +``` + +**For Production (e.g., Vercel):** + +- Set `NEXT_PUBLIC_API_URL` to `https://finquest-api.onrender.com` +- The API URL automatically switches based on the environment variable + +### Supabase OAuth Configuration + +For Google OAuth to work correctly in both development and production, you need to configure redirect URLs in your Supabase dashboard: + +1. Go to your Supabase project dashboard +2. Navigate to **Authentication** → **URL Configuration** +3. Add the following to **Redirect URLs**: + - `http://localhost:3000/` (for local development) + - `https://your-production-domain.com/` (for production) +4. Set the **Site URL** to your production URL (or leave it as default) + +**Important:** If localhost URLs are not whitelisted in Supabase, OAuth redirects will default to the Site URL, causing redirects to production even when running locally. + Then, run the development server: ````bash diff --git a/app/web/contexts/AuthContext.tsx b/app/web/contexts/AuthContext.tsx index b58367b..096a313 100644 --- a/app/web/contexts/AuthContext.tsx +++ b/app/web/contexts/AuthContext.tsx @@ -72,7 +72,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => { const { error } = await supabase.auth.signInWithOAuth({ provider: 'google', options: { - redirectTo: `${window.location.origin}/`, + redirectTo: process.env.NEXT_PUBLIC_SITE_URL, }, }); From 38937d7723c76590e26fbea0fdff76ea53bed4e8 Mon Sep 17 00:00:00 2001 From: Jason Wang <40612523+jasonfyw@users.noreply.github.com> Date: Tue, 25 Nov 2025 23:39:52 -0500 Subject: [PATCH 12/14] fix: simplified cors --- app/services/api/src/finquest_api/config.py | 25 ++++++++++++--------- app/services/api/src/finquest_api/main.py | 2 +- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/app/services/api/src/finquest_api/config.py b/app/services/api/src/finquest_api/config.py index 8978b11..2a1e1fd 100644 --- a/app/services/api/src/finquest_api/config.py +++ b/app/services/api/src/finquest_api/config.py @@ -31,18 +31,21 @@ class Settings(BaseSettings): API_VERSION: str = "0.1.0" DEBUG: bool = True - # CORS Configuration - stored as string, converted to list via property - ALLOWED_ORIGINS: str = "http://localhost:3000,http://localhost:3001,http://127.0.0.1:3000,https://tryfinquest.vercel.app" + # CORS Configuration + ALLOWED_ORIGINS: List[str] = [ + "http://localhost:3000", + "http://localhost:3001", + "http://127.0.0.1:3000", + "https://tryfinquest.vercel.app", + ] - @property - def allowed_origins_list(self) -> List[str]: - """Parse CORS origins from comma-separated string to list""" - if not self.ALLOWED_ORIGINS: - return [] - # Split by comma and strip whitespace, remove empty strings - origins = [origin.strip() for origin in self.ALLOWED_ORIGINS.split(",") if origin.strip()] - # Remove trailing slashes for consistency - return [origin.rstrip("/") for origin in origins] + @field_validator("ALLOWED_ORIGINS", mode="before") + @classmethod + def parse_allowed_origins(cls, v): + """Parse comma-separated string to list if needed""" + if isinstance(v, str): + return [origin.strip() for origin in v.split(",") if origin.strip()] + return v # Server Configuration HOST: str = "0.0.0.0" diff --git a/app/services/api/src/finquest_api/main.py b/app/services/api/src/finquest_api/main.py index b9b0f67..c3d2403 100644 --- a/app/services/api/src/finquest_api/main.py +++ b/app/services/api/src/finquest_api/main.py @@ -19,7 +19,7 @@ # Configure CORS app.add_middleware( CORSMiddleware, - allow_origins=settings.allowed_origins_list, + allow_origins=settings.ALLOWED_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], From d63db0ca04711b0eff1c1643d41217a34a515610 Mon Sep 17 00:00:00 2001 From: Jason Wang <40612523+jasonfyw@users.noreply.github.com> Date: Tue, 25 Nov 2025 23:46:10 -0500 Subject: [PATCH 13/14] Revert "fix: simplified cors" This reverts commit 38937d7723c76590e26fbea0fdff76ea53bed4e8. --- app/services/api/src/finquest_api/config.py | 25 +++++++++------------ app/services/api/src/finquest_api/main.py | 2 +- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/app/services/api/src/finquest_api/config.py b/app/services/api/src/finquest_api/config.py index 2a1e1fd..8978b11 100644 --- a/app/services/api/src/finquest_api/config.py +++ b/app/services/api/src/finquest_api/config.py @@ -31,21 +31,18 @@ class Settings(BaseSettings): API_VERSION: str = "0.1.0" DEBUG: bool = True - # CORS Configuration - ALLOWED_ORIGINS: List[str] = [ - "http://localhost:3000", - "http://localhost:3001", - "http://127.0.0.1:3000", - "https://tryfinquest.vercel.app", - ] + # CORS Configuration - stored as string, converted to list via property + ALLOWED_ORIGINS: str = "http://localhost:3000,http://localhost:3001,http://127.0.0.1:3000,https://tryfinquest.vercel.app" - @field_validator("ALLOWED_ORIGINS", mode="before") - @classmethod - def parse_allowed_origins(cls, v): - """Parse comma-separated string to list if needed""" - if isinstance(v, str): - return [origin.strip() for origin in v.split(",") if origin.strip()] - return v + @property + def allowed_origins_list(self) -> List[str]: + """Parse CORS origins from comma-separated string to list""" + if not self.ALLOWED_ORIGINS: + return [] + # Split by comma and strip whitespace, remove empty strings + origins = [origin.strip() for origin in self.ALLOWED_ORIGINS.split(",") if origin.strip()] + # Remove trailing slashes for consistency + return [origin.rstrip("/") for origin in origins] # Server Configuration HOST: str = "0.0.0.0" diff --git a/app/services/api/src/finquest_api/main.py b/app/services/api/src/finquest_api/main.py index c3d2403..b9b0f67 100644 --- a/app/services/api/src/finquest_api/main.py +++ b/app/services/api/src/finquest_api/main.py @@ -19,7 +19,7 @@ # Configure CORS app.add_middleware( CORSMiddleware, - allow_origins=settings.ALLOWED_ORIGINS, + allow_origins=settings.allowed_origins_list, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], From 3c4c6941efa40cc94cbf7435fc8070c97f5be1af Mon Sep 17 00:00:00 2001 From: Jason Wang <40612523+jasonfyw@users.noreply.github.com> Date: Tue, 25 Nov 2025 23:50:22 -0500 Subject: [PATCH 14/14] fix: lint error --- app/services/api/src/finquest_api/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/api/src/finquest_api/config.py b/app/services/api/src/finquest_api/config.py index 8978b11..f08fc32 100644 --- a/app/services/api/src/finquest_api/config.py +++ b/app/services/api/src/finquest_api/config.py @@ -3,7 +3,7 @@ """ from typing import List, Optional -from pydantic import BaseModel, SecretStr, field_validator +from pydantic import BaseModel, SecretStr from pydantic_settings import BaseSettings, SettingsConfigDict