Your Name commited on
Commit
e9b8ed2
·
1 Parent(s): 2464c4f

Safely handle and sanitize invalid database URLs

Browse files
Files changed (2) hide show
  1. backend/core/config.py +5 -0
  2. backend/db/session.py +26 -13
backend/core/config.py CHANGED
@@ -28,10 +28,15 @@ class Settings(BaseSettings):
28
  if not v or not isinstance(v, str) or not v.strip():
29
  return "sqlite+aiosqlite:////tmp/autoforge.db"
30
  v = v.strip()
 
 
 
31
  if v.startswith("postgres://"):
32
  return v.replace("postgres://", "postgresql+asyncpg://", 1)
33
  if v.startswith("postgresql://") and not v.startswith("postgresql+asyncpg://"):
34
  return v.replace("postgresql://", "postgresql+asyncpg://", 1)
 
 
35
  return v
36
  secret_key: str = "change-me-in-production-use-a-long-random-string"
37
  encryption_key: str = "change-me-32-byte-base64-fernet-key-here="
 
28
  if not v or not isinstance(v, str) or not v.strip():
29
  return "sqlite+aiosqlite:////tmp/autoforge.db"
30
  v = v.strip()
31
+ # If someone accidentally pasted an HTTP/HTTPS URL (e.g. dashboard URL)
32
+ if v.startswith("http://") or v.startswith("https://"):
33
+ return "sqlite+aiosqlite:////tmp/autoforge.db"
34
  if v.startswith("postgres://"):
35
  return v.replace("postgres://", "postgresql+asyncpg://", 1)
36
  if v.startswith("postgresql://") and not v.startswith("postgresql+asyncpg://"):
37
  return v.replace("postgresql://", "postgresql+asyncpg://", 1)
38
+ if not (v.startswith("sqlite") or v.startswith("postgresql")):
39
+ return "sqlite+aiosqlite:////tmp/autoforge.db"
40
  return v
41
  secret_key: str = "change-me-in-production-use-a-long-random-string"
42
  encryption_key: str = "change-me-32-byte-base64-fernet-key-here="
backend/db/session.py CHANGED
@@ -1,24 +1,37 @@
 
1
  from collections.abc import AsyncGenerator
2
 
3
  from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
4
 
5
  from backend.core.config import get_settings
6
 
 
7
  settings = get_settings()
8
 
9
- db_url = settings.database_url
10
- if db_url.startswith("sqlite"):
11
- engine = create_async_engine(
12
- db_url,
13
- echo=False,
14
- connect_args={"check_same_thread": False},
15
- )
16
- else:
17
- engine = create_async_engine(
18
- db_url,
19
- echo=False,
20
- pool_pre_ping=True,
21
- )
 
 
 
 
 
 
 
 
 
 
 
22
 
23
  AsyncSessionLocal = async_sessionmaker(
24
  engine,
 
1
+ import logging
2
  from collections.abc import AsyncGenerator
3
 
4
  from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
5
 
6
  from backend.core.config import get_settings
7
 
8
+ logger = logging.getLogger(__name__)
9
  settings = get_settings()
10
 
11
+
12
+ def _create_engine_safely(url: str):
13
+ try:
14
+ if url.startswith("sqlite"):
15
+ return create_async_engine(
16
+ url,
17
+ echo=False,
18
+ connect_args={"check_same_thread": False},
19
+ )
20
+ return create_async_engine(
21
+ url,
22
+ echo=False,
23
+ pool_pre_ping=True,
24
+ )
25
+ except Exception as exc:
26
+ logger.error("Failed to create engine for %s, falling back to SQLite: %s", url, exc)
27
+ return create_async_engine(
28
+ "sqlite+aiosqlite:////tmp/autoforge.db",
29
+ echo=False,
30
+ connect_args={"check_same_thread": False},
31
+ )
32
+
33
+
34
+ engine = _create_engine_safely(settings.database_url)
35
 
36
  AsyncSessionLocal = async_sessionmaker(
37
  engine,