Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added Backend/app/__init__.py
Empty file.
Empty file added Backend/app/db/__init__.py
Empty file.
100 changes: 88 additions & 12 deletions Backend/app/db/db.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,116 @@
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.pool import NullPool
from sqlalchemy import text
import asyncio
import os
from dotenv import load_dotenv

# Load environment variables from .env
load_dotenv()

# Fetch database credentials
USER = os.getenv("user")
PASSWORD = os.getenv("password")
HOST = os.getenv("host")
PORT = os.getenv("port")
DBNAME = os.getenv("dbname")
# Fetch database credentials and strip whitespace
USER = os.getenv("user", "").strip()
PASSWORD = os.getenv("password", "").strip()
HOST = os.getenv("host", "").strip()
PORT = os.getenv("port", "").strip()
DBNAME = os.getenv("dbname", "").strip()

# Corrected async SQLAlchemy connection string (removed `sslmode=require`)
# Construct async SQLAlchemy connection string with connection parameters
DATABASE_URL = f"postgresql+asyncpg://{USER}:{PASSWORD}@{HOST}:{PORT}/{DBNAME}"

# Initialize async SQLAlchemy components
try:
engine = create_async_engine(
DATABASE_URL, echo=True, connect_args={"ssl": "require"}
DATABASE_URL,
echo=False, # Reduce noise in logs
poolclass=NullPool,
future=True,
connect_args={
"server_settings": {
"application_name": "InPact_Backend",
},
"command_timeout": 30,
# Add SSL configuration for Supabase
"ssl": "require",
}
)

AsyncSessionLocal = sessionmaker(
bind=engine, class_=AsyncSession, expire_on_commit=False
bind=engine,
class_=AsyncSession,
expire_on_commit=False
)

Base = declarative_base()
print("βœ… Database connected successfully!")
print("βœ… Database engine created successfully!")
except SQLAlchemyError as e:
print(f"❌ Error connecting to the database: {e}")
print(f"❌ Error creating database engine: {e}")
engine = None
AsyncSessionLocal = None
Base = None


async def test_connection():
"""Test database connection with detailed diagnostics"""
try:
if engine is None:
return False, "Engine not initialized"

# Test DNS resolution first
import socket
try:
socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socket.SOCK_STREAM)
except socket.gaierror as dns_error:
if "11001" in str(dns_error):
return False, f"IPv6 connectivity issue detected for {HOST}. Your network may not support IPv6, but Supabase REST API is available as fallback."
else:
return False, f"DNS resolution failed: {dns_error}"

# Try database connection with timeout
try:
async with asyncio.wait_for(engine.begin(), timeout=10) as conn:
result = await conn.execute(text("SELECT 1"))
await result.fetchone()
return True, "PostgreSQL connection successful"
except asyncio.TimeoutError:
return False, "Connection timeout - database may be unreachable"

except Exception as e:
error_msg = str(e)
if "11001" in error_msg:
return False, f"IPv6 connectivity issue: Your system cannot resolve the IPv6-only Supabase database host. Using REST API fallback."
return False, f"Connection failed: {e}"


async def test_connection_with_retry(max_retries: int = 2):
"""Test connection with retry mechanism"""
for attempt in range(max_retries):
is_connected, message = await test_connection()
if is_connected:
return True, message

if attempt < max_retries - 1:
print(f"⚠️ Connection attempt {attempt + 1} failed: {message}")
print(f"πŸ”„ Retrying in 2 seconds...")
await asyncio.sleep(2)
else:
return False, message

return False, "All connection attempts failed"


async def get_db():
if AsyncSessionLocal is None:
raise Exception("Database not configured properly")

async with AsyncSessionLocal() as session:
yield session
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise
finally:
await session.close()
100 changes: 52 additions & 48 deletions Backend/app/db/seed.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,54 +4,58 @@


async def seed_db():
users = [
{
"id": "aabb1fd8-ba93-4e8c-976e-35e5c40b809c",
"username": "creator1",
"email": "[email protected]",
"password": "password123",
"role": "creator",
"bio": "Lifestyle and travel content creator",
"profile_image": None,
"created_at": datetime.utcnow()
},
{
"id": "6dbfcdd5-795f-49c1-8f7a-a5538b8c6f6f",
"username": "brand1",
"email": "[email protected]",
"password": "password123",
"role": "brand",
"bio": "Sustainable fashion brand looking for influencers",
"profile_image": None,
"created_at": datetime.utcnow()
},
]
try:
users = [
{
"id": "aabb1fd8-ba93-4e8c-976e-35e5c40b809c",
"username": "creator1",
"email": "[email protected]",
"password": "password123",
"role": "creator",
"bio": "Lifestyle and travel content creator",
"profile_image": None,
"created_at": datetime.utcnow()
},
{
"id": "6dbfcdd5-795f-49c1-8f7a-a5538b8c6f6f",
"username": "brand1",
"email": "[email protected]",
"password": "password123",
"role": "brand",
"bio": "Sustainable fashion brand looking for influencers",
"profile_image": None,
"created_at": datetime.utcnow()
},
]

# Insert or update the users
async with AsyncSessionLocal() as session:
for user_data in users:
# Check if user exists
existing_user = await session.execute(
User.__table__.select().where(User.email == user_data["email"])
)
existing_user = existing_user.scalar_one_or_none()

if existing_user:
continue
else:
# Create new user
user = User(
id=user_data["id"],
username=user_data["username"],
email=user_data["email"],
role=user_data["role"],
profile_image=user_data["profile_image"],
bio=user_data["bio"],
created_at=user_data["created_at"]
# Insert or update the users
async with AsyncSessionLocal() as session:
for user_data in users:
# Check if user exists
existing_user = await session.execute(
User.__table__.select().where(User.email == user_data["email"])
)
session.add(user)
print(f"Created user: {user_data['email']}")
existing_user = existing_user.scalar_one_or_none()

if existing_user:
continue
else:
# Create new user
user = User(
id=user_data["id"],
username=user_data["username"],
email=user_data["email"],
role=user_data["role"],
profile_image=user_data["profile_image"],
bio=user_data["bio"],
created_at=user_data["created_at"]
)
session.add(user)
print(f"Created user: {user_data['email']}")

# Commit the session
await session.commit()
print("βœ… Users seeded successfully.")
# Commit the session
await session.commit()
print("βœ… Users seeded successfully.")
except Exception as e:
print(f"⚠️ Database seeding failed: {e}")
print("Server will continue without seeded data.")
34 changes: 30 additions & 4 deletions Backend/app/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from .db.db import engine
from .db.db import engine, test_connection_with_retry
from .db.seed import seed_db
from .services.supabase_service import supabase_service
from .models import models, chat
from .routes.post import router as post_router
from .routes.chat import router as chat_router
Expand All @@ -24,16 +25,41 @@ async def create_tables():
await conn.run_sync(models.Base.metadata.create_all)
await conn.run_sync(chat.Base.metadata.create_all)
print("βœ… Tables created successfully or already exist.")
except SQLAlchemyError as e:
except Exception as e:
print(f"❌ Error creating tables: {e}")
print("⚠️ Database connection failed. Server will start without database functionality.")


# Lifespan context manager for startup and shutdown events
@asynccontextmanager
async def lifespan(app: FastAPI):
print("App is starting...")
await create_tables()
await seed_db()

# Test PostgreSQL connection first
print("πŸ” Testing PostgreSQL database connection...")
is_connected, message = await test_connection_with_retry(max_retries=2)

if is_connected:
print(f"βœ… PostgreSQL connection: {message}")
try:
await create_tables()
await seed_db()
print("βœ… Database initialization completed successfully!")
except Exception as e:
print(f"⚠️ Database initialization error: {e}")
else:
print(f"ℹ️ PostgreSQL unavailable: {message}")
print("πŸ”„ Switching to Supabase REST API...")

# Try Supabase REST API as fallback
supabase_connected = await supabase_service.connect()
if supabase_connected:
print("βœ… Using Supabase REST API for database operations")
await supabase_service.create_tables()
await supabase_service.seed_data()
Comment on lines +54 to +59
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

Handle potential errors from Supabase fallback operations.

If supabase_service.create_tables() or seed_data() fail, the exceptions are not caught, potentially crashing the startup. Consider wrapping these in a try/except for consistency with the PostgreSQL path.

         supabase_connected = await supabase_service.connect()
         if supabase_connected:
             print("βœ… Using Supabase REST API for database operations")
-            await supabase_service.create_tables()
-            await supabase_service.seed_data()
+            try:
+                await supabase_service.create_tables()
+                await supabase_service.seed_data()
+            except Exception as e:
+                print(f"⚠️ Supabase initialization error: {e}")
         else:
             print("πŸš€ Server starting in limited mode without database...")
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Try Supabase REST API as fallback
supabase_connected = await supabase_service.connect()
if supabase_connected:
print("βœ… Using Supabase REST API for database operations")
await supabase_service.create_tables()
await supabase_service.seed_data()
# Try Supabase REST API as fallback
supabase_connected = await supabase_service.connect()
if supabase_connected:
print("βœ… Using Supabase REST API for database operations")
try:
await supabase_service.create_tables()
await supabase_service.seed_data()
except Exception as e:
print(f"⚠️ Supabase initialization error: {e}")
πŸ€– Prompt for AI Agents
In Backend/app/main.py around lines 54 to 59, the supabase fallback calls to
supabase_service.create_tables() and supabase_service.seed_data() are unguarded
and can raise exceptions; wrap these calls in a try/except block that catches
Exception, logs the error with context (including exception details), and
handles failure the same way as the PostgreSQL path (e.g., exit the process or
return a non-success status) to prevent an unhandled crash during startup.

else:
print("πŸš€ Server starting in limited mode without database...")

yield
print("App is shutting down...")

Expand Down
Empty file added Backend/app/models/__init__.py
Empty file.
Empty file added Backend/app/routes/__init__.py
Empty file.
Loading