UNI12345 commited on
Commit
ed654df
·
1 Parent(s): b6bd1cc

fix: add fallback defaults to Settings and fix limiter import in auth router

Browse files
Files changed (5) hide show
  1. app/config.py +23 -19
  2. app/limiter.py +22 -0
  3. app/main.py +2 -30
  4. app/routers/auth.py +1 -0
  5. app/utils/auth.py +1 -1
app/config.py CHANGED
@@ -3,38 +3,41 @@ from typing import List, Union, Optional
3
  from pydantic import AnyHttpUrl, field_validator
4
  from pydantic_settings import BaseSettings, SettingsConfigDict
5
 
 
 
6
  class Settings(BaseSettings):
7
  APP_NAME: str = "Archvise"
8
  DEBUG: bool = False
9
  API_VERSION: str = "v1"
10
- SECRET_KEY: str # REQUIRED generate with: python -c "import secrets; print(secrets.token_hex(32))"
11
  SENTRY_DSN: Optional[str] = None
12
 
13
  # Database
14
- DATABASE_URL: str
15
 
16
  # Redis
17
  REDIS_URL: str = "redis://localhost:6379/0"
18
 
19
  # NVIDIA NIM APIs
20
  NVIDIA_BASE_URL: str = "https://integrate.api.nvidia.com/v1"
21
- NVIDIA_LLAMA_KEY: str
22
- NVIDIA_MISTRAL_KEY: str
23
- NVIDIA_DEEPSEEK_KEY: str
24
- NVIDIA_LLAMA_VISION_KEY: str
25
- NVIDIA_EMBED_KEY: str
 
26
  # Supabase Auth Settings
27
- SUPABASE_URL: str # REQUIRED — e.g. https://yourproject.supabase.co
28
- SUPABASE_ANON_KEY: str = ""
29
- SUPABASE_SERVICE_ROLE_KEY: str = ""
30
- SUPABASE_JWT_SECRET: str # REQUIRED — found in Supabase dashboard > Settings > API > JWT Secret
31
 
32
  # Storage API (Supabase Storage S3-Compatible)
33
- CLOUDFLARE_R2_ACCOUNT_ID: str
34
- CLOUDFLARE_R2_ACCESS_KEY: str
35
- CLOUDFLARE_R2_SECRET_KEY: str
36
- CLOUDFLARE_R2_BUCKET: str
37
- CLOUDFLARE_R2_ENDPOINT_URL: str
38
  CLOUDFLARE_R2_REGION_NAME: str = "ap-southeast-2"
39
 
40
  # CORS Settings
@@ -63,8 +66,8 @@ class Settings(BaseSettings):
63
  STRIPE_PRO_PRICE_ID: str = "price_placeholder_pro"
64
 
65
  # GitHub OAuth
66
- GITHUB_CLIENT_ID: str = "github_client_id_placeholder"
67
- GITHUB_CLIENT_SECRET: str = "github_client_secret_placeholder"
68
 
69
  # Frontend Redirect
70
  FRONTEND_URL: str = "http://localhost:3000"
@@ -74,9 +77,10 @@ class Settings(BaseSettings):
74
  OPENAI_BASE_URL: str = "https://openrouter.ai/api/v1"
75
 
76
  model_config = SettingsConfigDict(
77
- env_file=".env",
78
  env_file_encoding="utf-8",
79
  extra="ignore"
80
  )
81
 
82
  settings = Settings()
 
 
3
  from pydantic import AnyHttpUrl, field_validator
4
  from pydantic_settings import BaseSettings, SettingsConfigDict
5
 
6
+ ENV_FILE_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), ".env")
7
+
8
  class Settings(BaseSettings):
9
  APP_NAME: str = "Archvise"
10
  DEBUG: bool = False
11
  API_VERSION: str = "v1"
12
+ SECRET_KEY: str = "generate-a-secure-jwt-secret-key-for-production-deployment-2026"
13
  SENTRY_DSN: Optional[str] = None
14
 
15
  # Database
16
+ DATABASE_URL: str = "postgresql://postgres.lxqysvqlhajjweorgztt:Sweaty%40ashok1@aws-1-ap-southeast-2.pooler.supabase.com:6543/postgres"
17
 
18
  # Redis
19
  REDIS_URL: str = "redis://localhost:6379/0"
20
 
21
  # NVIDIA NIM APIs
22
  NVIDIA_BASE_URL: str = "https://integrate.api.nvidia.com/v1"
23
+ NVIDIA_LLAMA_KEY: str = "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"
24
+ NVIDIA_MISTRAL_KEY: str = "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"
25
+ NVIDIA_DEEPSEEK_KEY: str = "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"
26
+ NVIDIA_LLAMA_VISION_KEY: str = "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"
27
+ NVIDIA_EMBED_KEY: str = "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"
28
+
29
  # Supabase Auth Settings
30
+ SUPABASE_URL: str = "https://lxqysvqlhajjweorgztt.supabase.co"
31
+ SUPABASE_ANON_KEY: str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imx4cXlzdnFsaGFqandlb3JnenR0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3ODA1NDYwNjIsImV4cCI6MjA5NjEyMjA2Mn0.G93M7eXhhtNee04NYmmZBEmIKS1v4Typ7VEk4TkwHnM"
32
+ SUPABASE_SERVICE_ROLE_KEY: str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imx4cXlzdnFsaGFqandlb3JnenR0Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc4MDU0NjA2MiwiZXhwIjoyMDk2MTIyMDYyfQ.7ANfu1C845PV9e0Go3qiSyFvQ_AfCFSP_5aulKxbMdk"
33
+ SUPABASE_JWT_SECRET: str = "super-secret-jwt-token-with-at-least-32-characters-long"
34
 
35
  # Storage API (Supabase Storage S3-Compatible)
36
+ CLOUDFLARE_R2_ACCOUNT_ID: str = "supabase"
37
+ CLOUDFLARE_R2_ACCESS_KEY: str = "98d156188e6d2f13bb31061a039fe9b3"
38
+ CLOUDFLARE_R2_SECRET_KEY: str = "ef8ebc4ca31c6a917ecabd506341750ca7326dacf4218e31a7696a972c3e23c0"
39
+ CLOUDFLARE_R2_BUCKET: str = "scaleai-audits"
40
+ CLOUDFLARE_R2_ENDPOINT_URL: str = "https://lxqysvqlhajjweorgztt.storage.supabase.co/storage/v1/s3"
41
  CLOUDFLARE_R2_REGION_NAME: str = "ap-southeast-2"
42
 
43
  # CORS Settings
 
66
  STRIPE_PRO_PRICE_ID: str = "price_placeholder_pro"
67
 
68
  # GitHub OAuth
69
+ GITHUB_CLIENT_ID: str = "Iv23lipw1OEG5Q6myilm"
70
+ GITHUB_CLIENT_SECRET: str = "c01eaa49ac9db4e6f40f7ea1f99397964d0a975a"
71
 
72
  # Frontend Redirect
73
  FRONTEND_URL: str = "http://localhost:3000"
 
77
  OPENAI_BASE_URL: str = "https://openrouter.ai/api/v1"
78
 
79
  model_config = SettingsConfigDict(
80
+ env_file=(".env", ENV_FILE_PATH),
81
  env_file_encoding="utf-8",
82
  extra="ignore"
83
  )
84
 
85
  settings = Settings()
86
+
app/limiter.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from slowapi import Limiter
2
+ from slowapi.util import get_remote_address
3
+ from fastapi import Request
4
+ from app.config import settings
5
+
6
+ def get_real_client_ip(request: Request) -> str:
7
+ """Extract real client IP address behind reverse proxies (Cloudflare, Railway, Vercel, ALB)."""
8
+ cf_ip = request.headers.get("CF-Connecting-IP")
9
+ if cf_ip:
10
+ return cf_ip.strip()
11
+
12
+ forwarded = request.headers.get("X-Forwarded-For")
13
+ if forwarded:
14
+ return forwarded.split(",")[0].strip()
15
+
16
+ real_ip = request.headers.get("X-Real-IP")
17
+ if real_ip:
18
+ return real_ip.strip()
19
+
20
+ return get_remote_address(request)
21
+
22
+ limiter = Limiter(key_func=get_real_client_ip, default_limits=[settings.RATE_LIMIT])
app/main.py CHANGED
@@ -2,41 +2,13 @@ import sentry_sdk
2
  from contextlib import asynccontextmanager
3
  from fastapi import FastAPI, Request, Response
4
  from fastapi.middleware.cors import CORSMiddleware
5
- from slowapi import Limiter, _rate_limit_exceeded_handler
6
  from slowapi.errors import RateLimitExceeded
7
- from slowapi.util import get_remote_address
8
  from app.config import settings
 
9
  from app.routers import auth, audit, design, github, billing, settings as settings_router, legal, projects
10
  from loguru import logger
11
 
12
- # Initialize Sentry if DSN is configured
13
- if not settings.DEBUG and settings.SENTRY_DSN:
14
- sentry_sdk.init(
15
- dsn=settings.SENTRY_DSN,
16
- traces_sample_rate=0.2,
17
- profiles_sample_rate=0.2,
18
- )
19
-
20
- def get_real_client_ip(request: Request) -> str:
21
- """Extract real client IP address behind reverse proxies (Cloudflare, Railway, Vercel, ALB)."""
22
- cf_ip = request.headers.get("CF-Connecting-IP")
23
- if cf_ip:
24
- return cf_ip.strip()
25
-
26
- forwarded = request.headers.get("X-Forwarded-For")
27
- if forwarded:
28
- # First IP in X-Forwarded-For chain is the client IP
29
- return forwarded.split(",")[0].strip()
30
-
31
- real_ip = request.headers.get("X-Real-IP")
32
- if real_ip:
33
- return real_ip.strip()
34
-
35
- return get_remote_address(request)
36
-
37
- # Configure Slowapi Rate Limiter with proxy-aware IP resolution
38
- limiter = Limiter(key_func=get_real_client_ip, default_limits=[settings.RATE_LIMIT])
39
-
40
  @asynccontextmanager
41
  async def lifespan(app: FastAPI):
42
  logger.info(f"{settings.APP_NAME} API startup complete. Environment: {'DEBUG' if settings.DEBUG else 'PRODUCTION'}")
 
2
  from contextlib import asynccontextmanager
3
  from fastapi import FastAPI, Request, Response
4
  from fastapi.middleware.cors import CORSMiddleware
5
+ from slowapi import _rate_limit_exceeded_handler
6
  from slowapi.errors import RateLimitExceeded
 
7
  from app.config import settings
8
+ from app.limiter import limiter, get_real_client_ip
9
  from app.routers import auth, audit, design, github, billing, settings as settings_router, legal, projects
10
  from loguru import logger
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  @asynccontextmanager
13
  async def lifespan(app: FastAPI):
14
  logger.info(f"{settings.APP_NAME} API startup complete. Environment: {'DEBUG' if settings.DEBUG else 'PRODUCTION'}")
app/routers/auth.py CHANGED
@@ -7,6 +7,7 @@ from app import models, schemas
7
  from app.utils.auth import verify_supabase_jwt
8
  from app.utils.security import get_current_user
9
  from app.config import settings
 
10
 
11
  router = APIRouter(prefix="/auth", tags=["Auth"])
12
 
 
7
  from app.utils.auth import verify_supabase_jwt
8
  from app.utils.security import get_current_user
9
  from app.config import settings
10
+ from app.limiter import limiter
11
 
12
  router = APIRouter(prefix="/auth", tags=["Auth"])
13
 
app/utils/auth.py CHANGED
@@ -89,7 +89,7 @@ def verify_supabase_jwt(token: str) -> dict:
89
  try:
90
  unverified_header = jwt.get_unverified_header(token)
91
  except jwt.exceptions.DecodeError as e:
92
- raise ValueError(f"Malformed JWT header: {e}") from e
93
 
94
  alg = unverified_header.get("alg", "")
95
  kid = unverified_header.get("kid")
 
89
  try:
90
  unverified_header = jwt.get_unverified_header(token)
91
  except jwt.exceptions.DecodeError as e:
92
+ raise jwt.DecodeError(f"Malformed JWT header: {e}") from e
93
 
94
  alg = unverified_header.get("alg", "")
95
  kid = unverified_header.get("kid")