asnannp commited on
Commit
fba198b
·
1 Parent(s): 5c54f74

deploy: sync backend to Space root (learn-lesson HF cache fix)

Browse files
app/core/config.py CHANGED
@@ -13,12 +13,6 @@ class Settings(BaseSettings):
13
  app_name: str = "AI Exam Success API"
14
  app_version: str = "0.1.0"
15
  environment: str = "development"
16
-
17
- @property
18
- def is_production(self) -> bool:
19
- """Single source for production-only cookie and security behaviour."""
20
- return self.environment.strip().casefold() == "production"
21
-
22
  database_url: str = f"sqlite:///{BACKEND_DIR / 'exam_success_dev.db'}"
23
  # Dev defaults stay small; production .env should raise these for 1k+ concurrent students.
24
  database_pool_size: int = Field(
@@ -310,6 +304,11 @@ class Settings(BaseSettings):
310
  extra="ignore",
311
  )
312
 
 
 
 
 
 
313
  @property
314
  def cors_origin_list(self) -> list[str]:
315
  origins = [origin.strip() for origin in self.cors_origins.split(",") if origin.strip()]
 
13
  app_name: str = "AI Exam Success API"
14
  app_version: str = "0.1.0"
15
  environment: str = "development"
 
 
 
 
 
 
16
  database_url: str = f"sqlite:///{BACKEND_DIR / 'exam_success_dev.db'}"
17
  # Dev defaults stay small; production .env should raise these for 1k+ concurrent students.
18
  database_pool_size: int = Field(
 
304
  extra="ignore",
305
  )
306
 
307
+ @property
308
+ def is_production(self) -> bool:
309
+ """True when ENVIRONMENT is production (case-insensitive)."""
310
+ return self.environment.strip().casefold() == "production"
311
+
312
  @property
313
  def cors_origin_list(self) -> list[str]:
314
  origins = [origin.strip() for origin in self.cors_origins.split(",") if origin.strip()]
app/routes/auth.py CHANGED
@@ -267,12 +267,18 @@ def _auth_redirect_for_user(user: User, next_path: str) -> RedirectResponse:
267
 
268
 
269
  def _is_production_environment(settings: object | None = None) -> bool:
270
- """Resolve production safely even if an older Settings build lacks is_production."""
271
  active = settings or get_settings()
272
- flag = getattr(active, "is_production", None)
 
 
 
273
  if isinstance(flag, bool):
274
- return flag
275
- return str(getattr(active, "environment", "") or "").strip().casefold() == "production"
 
 
 
276
 
277
 
278
  def _set_media_auth_cookie(
 
267
 
268
 
269
  def _is_production_environment(settings: object | None = None) -> bool:
270
+ """Resolve production even if an older Settings build lacks is_production."""
271
  active = settings or get_settings()
272
+ try:
273
+ flag = getattr(active, "is_production", None)
274
+ except AttributeError:
275
+ flag = None
276
  if isinstance(flag, bool):
277
+ production = flag
278
+ else:
279
+ production = str(getattr(active, "environment", "") or "").strip().casefold() == "production"
280
+ logger.debug("[FIX] media-auth cookie production=%s", production)
281
+ return production
282
 
283
 
284
  def _set_media_auth_cookie(
app/routes/chat.py CHANGED
The diff for this file is too large to render. See raw diff
 
tests/test_media_auth_cookie.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Regression tests for signup media-cookie setup.
2
+
3
+ CI failed because `_set_media_auth_cookie` read `settings.is_production`
4
+ while `Settings` only exposed `environment`.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from types import SimpleNamespace
10
+
11
+ from fastapi import Response
12
+
13
+
14
+ def test_settings_exposes_is_production_from_environment() -> None:
15
+ from app.core.config import Settings
16
+
17
+ assert Settings(environment="production").is_production is True
18
+ assert Settings(environment="PRODUCTION").is_production is True
19
+ assert Settings(environment="development").is_production is False
20
+ assert Settings(environment=" staging ").is_production is False
21
+
22
+
23
+ def test_signup_sets_media_cookie_without_crashing(auth_client) -> None:
24
+ response = auth_client.post(
25
+ "/auth/signup",
26
+ json={
27
+ "name": "Cookie Student",
28
+ "email": "media-cookie-fix@example.test",
29
+ "password": "Pass123!beta",
30
+ },
31
+ )
32
+ assert response.status_code == 201, response.text
33
+ assert response.json()["access_token"]
34
+ cookie = response.headers.get("set-cookie", "")
35
+ assert "docdoe_media_token=" in cookie
36
+ assert "secure" not in cookie.lower()
37
+
38
+
39
+ def test_media_cookie_is_secure_when_environment_is_production() -> None:
40
+ from app.routes.auth import _set_media_auth_cookie
41
+
42
+ response = Response()
43
+ _set_media_auth_cookie(
44
+ response,
45
+ "token-value",
46
+ 3600,
47
+ settings=SimpleNamespace(environment="production"),
48
+ )
49
+ cookie = response.headers.get("set-cookie", "")
50
+ assert "docdoe_media_token=token-value" in cookie
51
+ assert "samesite=none" in cookie.lower()
52
+ assert "secure" in cookie.lower()