feat: implement configuration and database session setup for NeonDB and Redis
Browse files- backend/app/config/settings.py +40 -1
- backend/app/database/session.py +31 -1
- pyproject.toml +2 -0
- uv.lock +44 -0
backend/app/config/settings.py
CHANGED
|
@@ -1 +1,40 @@
|
|
| 1 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
| 2 |
+
from pydantic import Field
|
| 3 |
+
|
| 4 |
+
class Settings(BaseSettings):
|
| 5 |
+
PROJECT_NAME: str= "QuantIQ"
|
| 6 |
+
API_V1_STR: str= "/api/v1"
|
| 7 |
+
|
| 8 |
+
# Required environment variables (must be present in your .env file)
|
| 9 |
+
DATABASE_URL: str
|
| 10 |
+
REDIS_URL: str
|
| 11 |
+
GEMINI_API_KEY: str
|
| 12 |
+
|
| 13 |
+
# Optional Razorpay Credentials (defaults to empty string if not set)
|
| 14 |
+
RAZORPAY_KEY_ID: str= Field(default="")
|
| 15 |
+
RAZORPAY_KEY_SECRET: str= Field(default="")
|
| 16 |
+
RAZORPAY_WEBHOOK_SECRET: str= Field(default="")
|
| 17 |
+
|
| 18 |
+
# Auth configuration
|
| 19 |
+
SECRET_KEY: str= Field(default="supersecretkeychangeinproduction")
|
| 20 |
+
ACCESS_TOKEN_EXPIRE_MINUTES: int= 60*24*8
|
| 21 |
+
|
| 22 |
+
@property
|
| 23 |
+
def database_url_async(self) -> str:
|
| 24 |
+
"""
|
| 25 |
+
Ensures the database URL uses the asyncpg driver dialect.
|
| 26 |
+
Converts 'postgresql://' to 'postgresql+asyncpg://'
|
| 27 |
+
"""
|
| 28 |
+
url= self.DATABASE_URL
|
| 29 |
+
if url.startswith("postgresql://"):
|
| 30 |
+
return url.replace("postgresql://", "postgresql+asyncpg://", 1)
|
| 31 |
+
return url
|
| 32 |
+
|
| 33 |
+
model_config= SettingsConfigDict(
|
| 34 |
+
env_file=".env",
|
| 35 |
+
env_file_encoding="utf-8",
|
| 36 |
+
case_sensitive=True,
|
| 37 |
+
extra="ignore"
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
settings= Settings()
|
backend/app/database/session.py
CHANGED
|
@@ -1 +1,31 @@
|
|
| 1 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
|
| 2 |
+
from sqlalchemy.orm import DeclarativeBase
|
| 3 |
+
from backend.app.config.settings import settings
|
| 4 |
+
|
| 5 |
+
# Create async engine for Postgres using the resolved async URL
|
| 6 |
+
engine= create_async_engine(
|
| 7 |
+
settings.database_url_async,
|
| 8 |
+
echo= False,
|
| 9 |
+
future= True
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
# Create session factory
|
| 13 |
+
SessionLocal= async_sessionmaker(
|
| 14 |
+
bind= engine,
|
| 15 |
+
autocommit= False,
|
| 16 |
+
autoflush= False,
|
| 17 |
+
expire_on_commit= False,
|
| 18 |
+
class_= AsyncSession
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
# Declarative base for SQLAlchemy models
|
| 22 |
+
class Base(DeclarativeBase):
|
| 23 |
+
pass
|
| 24 |
+
|
| 25 |
+
# Dependency to retrieve a database session for requests
|
| 26 |
+
async def get_db():
|
| 27 |
+
async with SessionLocal() as session:
|
| 28 |
+
try:
|
| 29 |
+
yield session
|
| 30 |
+
finally:
|
| 31 |
+
await session.close()
|
pyproject.toml
CHANGED
|
@@ -19,4 +19,6 @@ dependencies = [
|
|
| 19 |
"google-generativeai>=0.4.1",
|
| 20 |
"pydantic-settings>=2.2.1",
|
| 21 |
"prometheus-fastapi-instrumentator>=7.0.0",
|
|
|
|
|
|
|
| 22 |
]
|
|
|
|
| 19 |
"google-generativeai>=0.4.1",
|
| 20 |
"pydantic-settings>=2.2.1",
|
| 21 |
"prometheus-fastapi-instrumentator>=7.0.0",
|
| 22 |
+
"razorpay>=1.4.1",
|
| 23 |
+
"httpx>=0.27.0",
|
| 24 |
]
|
uv.lock
CHANGED
|
@@ -644,6 +644,19 @@ wheels = [
|
|
| 644 |
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
|
| 645 |
]
|
| 646 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 647 |
[[package]]
|
| 648 |
name = "httplib2"
|
| 649 |
version = "0.31.2"
|
|
@@ -692,6 +705,21 @@ wheels = [
|
|
| 692 |
{ url = "https://files.pythonhosted.org/packages/48/63/b906c01e53f50d432c0defe43ce52764a111dc1bdd028bafbeb54dcfd008/httptools-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:384c17174464c8e873398b7af24f0b1f44d992c820328413951a625323155d77", size = 108209, upload-time = "2026-05-25T22:17:39.473Z" },
|
| 693 |
]
|
| 694 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 695 |
[[package]]
|
| 696 |
name = "idna"
|
| 697 |
version = "3.18"
|
|
@@ -1481,12 +1509,14 @@ dependencies = [
|
|
| 1481 |
{ name = "asyncpg" },
|
| 1482 |
{ name = "fastapi" },
|
| 1483 |
{ name = "google-generativeai" },
|
|
|
|
| 1484 |
{ name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" },
|
| 1485 |
{ name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" },
|
| 1486 |
{ name = "pandas-ta" },
|
| 1487 |
{ name = "prometheus-fastapi-instrumentator" },
|
| 1488 |
{ name = "psycopg2-binary" },
|
| 1489 |
{ name = "pydantic-settings" },
|
|
|
|
| 1490 |
{ name = "redis" },
|
| 1491 |
{ name = "sqlalchemy", extra = ["mypy"] },
|
| 1492 |
{ name = "uvicorn", extra = ["standard"] },
|
|
@@ -1500,11 +1530,13 @@ requires-dist = [
|
|
| 1500 |
{ name = "asyncpg", specifier = ">=0.29.0" },
|
| 1501 |
{ name = "fastapi", specifier = ">=0.110.0" },
|
| 1502 |
{ name = "google-generativeai", specifier = ">=0.4.1" },
|
|
|
|
| 1503 |
{ name = "pandas", specifier = ">=2.2.1" },
|
| 1504 |
{ name = "pandas-ta", specifier = ">=0.3.14b0" },
|
| 1505 |
{ name = "prometheus-fastapi-instrumentator", specifier = ">=7.0.0" },
|
| 1506 |
{ name = "psycopg2-binary", specifier = ">=2.9.9" },
|
| 1507 |
{ name = "pydantic-settings", specifier = ">=2.2.1" },
|
|
|
|
| 1508 |
{ name = "redis", specifier = ">=5.0.3" },
|
| 1509 |
{ name = "sqlalchemy", extras = ["mypy"], specifier = ">=2.0.28" },
|
| 1510 |
{ name = "uvicorn", extras = ["standard"], specifier = ">=0.28.0" },
|
|
@@ -1512,6 +1544,18 @@ requires-dist = [
|
|
| 1512 |
{ name = "yfinance", specifier = ">=0.2.37" },
|
| 1513 |
]
|
| 1514 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1515 |
[[package]]
|
| 1516 |
name = "redis"
|
| 1517 |
version = "8.0.0"
|
|
|
|
| 644 |
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
|
| 645 |
]
|
| 646 |
|
| 647 |
+
[[package]]
|
| 648 |
+
name = "httpcore"
|
| 649 |
+
version = "1.0.9"
|
| 650 |
+
source = { registry = "https://pypi.org/simple" }
|
| 651 |
+
dependencies = [
|
| 652 |
+
{ name = "certifi" },
|
| 653 |
+
{ name = "h11" },
|
| 654 |
+
]
|
| 655 |
+
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
|
| 656 |
+
wheels = [
|
| 657 |
+
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
|
| 658 |
+
]
|
| 659 |
+
|
| 660 |
[[package]]
|
| 661 |
name = "httplib2"
|
| 662 |
version = "0.31.2"
|
|
|
|
| 705 |
{ url = "https://files.pythonhosted.org/packages/48/63/b906c01e53f50d432c0defe43ce52764a111dc1bdd028bafbeb54dcfd008/httptools-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:384c17174464c8e873398b7af24f0b1f44d992c820328413951a625323155d77", size = 108209, upload-time = "2026-05-25T22:17:39.473Z" },
|
| 706 |
]
|
| 707 |
|
| 708 |
+
[[package]]
|
| 709 |
+
name = "httpx"
|
| 710 |
+
version = "0.28.1"
|
| 711 |
+
source = { registry = "https://pypi.org/simple" }
|
| 712 |
+
dependencies = [
|
| 713 |
+
{ name = "anyio" },
|
| 714 |
+
{ name = "certifi" },
|
| 715 |
+
{ name = "httpcore" },
|
| 716 |
+
{ name = "idna" },
|
| 717 |
+
]
|
| 718 |
+
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
|
| 719 |
+
wheels = [
|
| 720 |
+
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
|
| 721 |
+
]
|
| 722 |
+
|
| 723 |
[[package]]
|
| 724 |
name = "idna"
|
| 725 |
version = "3.18"
|
|
|
|
| 1509 |
{ name = "asyncpg" },
|
| 1510 |
{ name = "fastapi" },
|
| 1511 |
{ name = "google-generativeai" },
|
| 1512 |
+
{ name = "httpx" },
|
| 1513 |
{ name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" },
|
| 1514 |
{ name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" },
|
| 1515 |
{ name = "pandas-ta" },
|
| 1516 |
{ name = "prometheus-fastapi-instrumentator" },
|
| 1517 |
{ name = "psycopg2-binary" },
|
| 1518 |
{ name = "pydantic-settings" },
|
| 1519 |
+
{ name = "razorpay" },
|
| 1520 |
{ name = "redis" },
|
| 1521 |
{ name = "sqlalchemy", extra = ["mypy"] },
|
| 1522 |
{ name = "uvicorn", extra = ["standard"] },
|
|
|
|
| 1530 |
{ name = "asyncpg", specifier = ">=0.29.0" },
|
| 1531 |
{ name = "fastapi", specifier = ">=0.110.0" },
|
| 1532 |
{ name = "google-generativeai", specifier = ">=0.4.1" },
|
| 1533 |
+
{ name = "httpx", specifier = ">=0.27.0" },
|
| 1534 |
{ name = "pandas", specifier = ">=2.2.1" },
|
| 1535 |
{ name = "pandas-ta", specifier = ">=0.3.14b0" },
|
| 1536 |
{ name = "prometheus-fastapi-instrumentator", specifier = ">=7.0.0" },
|
| 1537 |
{ name = "psycopg2-binary", specifier = ">=2.9.9" },
|
| 1538 |
{ name = "pydantic-settings", specifier = ">=2.2.1" },
|
| 1539 |
+
{ name = "razorpay", specifier = ">=1.4.1" },
|
| 1540 |
{ name = "redis", specifier = ">=5.0.3" },
|
| 1541 |
{ name = "sqlalchemy", extras = ["mypy"], specifier = ">=2.0.28" },
|
| 1542 |
{ name = "uvicorn", extras = ["standard"], specifier = ">=0.28.0" },
|
|
|
|
| 1544 |
{ name = "yfinance", specifier = ">=0.2.37" },
|
| 1545 |
]
|
| 1546 |
|
| 1547 |
+
[[package]]
|
| 1548 |
+
name = "razorpay"
|
| 1549 |
+
version = "2.0.1"
|
| 1550 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1551 |
+
dependencies = [
|
| 1552 |
+
{ name = "requests" },
|
| 1553 |
+
]
|
| 1554 |
+
sdist = { url = "https://files.pythonhosted.org/packages/d1/e4/877eb8ba68bb5212021b25a4f97b6c3f693e08bb6b319fc97c966c4391a3/razorpay-2.0.1.tar.gz", hash = "sha256:a766b9019e496c995906f75c31900bff8b2709bea251f1771925d742cba53e5b", size = 197169, upload-time = "2026-03-09T12:59:03.445Z" }
|
| 1555 |
+
wheels = [
|
| 1556 |
+
{ url = "https://files.pythonhosted.org/packages/7f/35/e8fbf239cb16595007839408f7645bc952d8490f5dd50f9049434f296ce8/razorpay-2.0.1-py3-none-any.whl", hash = "sha256:e6a60c78203a3e1d3fc428e6fadf15bc67f75bd67217eaf9dc9925c87e98bc7a", size = 176920, upload-time = "2026-03-09T12:59:01.99Z" },
|
| 1557 |
+
]
|
| 1558 |
+
|
| 1559 |
[[package]]
|
| 1560 |
name = "redis"
|
| 1561 |
version = "8.0.0"
|