Spaces:
Sleeping
Sleeping
Your Name commited on
Commit ·
e9b8ed2
1
Parent(s): 2464c4f
Safely handle and sanitize invalid database URLs
Browse files- backend/core/config.py +5 -0
- 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 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 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,
|