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
9 changes: 8 additions & 1 deletion Backend/app/db/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@

# Initialize async SQLAlchemy components
try:
engine = create_async_engine(DATABASE_URL, echo=True, connect_args={"ssl": "require"})
engine = create_async_engine(
DATABASE_URL, echo=True, connect_args={"ssl": "require"}
)

AsyncSessionLocal = sessionmaker(
bind=engine, class_=AsyncSession, expire_on_commit=False
Expand All @@ -31,3 +33,8 @@
engine = None
AsyncSessionLocal = None
Base = None


async def get_db():
async with AsyncSessionLocal() as session:
yield session
54 changes: 54 additions & 0 deletions Backend/app/db/seed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from datetime import datetime, timezone
from db.db import AsyncSessionLocal
from models.models import User


async def seed_db():
users = [
{
"id": "aabb1fd8-ba93-4e8c-976e-35e5c40b809c",
"username": "creator1",
"email": "[email protected]",
"password": "password123",
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

Critical security issue: Plain text passwords are being stored.

The code is storing plain text passwords directly in the password_hash field without any hashing. This creates a serious security vulnerability where user credentials are exposed in the database.

Hash the passwords before storing them:

+from passlib.context import CryptContext
+
+pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+
 async def seed_db():
     users = [
         {
             "id": "aabb1fd8-ba93-4e8c-976e-35e5c40b809c",
             "username": "creator1",
             "email": "[email protected]",
-            "password": "password123",
+            "password": pwd_context.hash("password123"),
             "role": "creator",
             "bio": "Lifestyle and travel content creator",
         },
         {
             "id": "6dbfcdd5-795f-49c1-8f7a-a5538b8c6f6f",
             "username": "brand1",
             "email": "[email protected]",
-            "password": "password123",
+            "password": pwd_context.hash("password123"),
             "role": "brand",
             "bio": "Sustainable fashion brand looking for influencers",
         },
     ]

     # ... rest of the code ...
                 user = User(
                     id=user_data["id"],
                     username=user_data["username"],
                     email=user_data["email"],
-                    password_hash=user_data[
-                        "password"
-                    ],  # Using plain password directly
+                    password_hash=user_data["password"],  # Now properly hashed
                     role=user_data["role"],
                     bio=user_data["bio"],
                 )

Also applies to: 20-20, 43-45

🤖 Prompt for AI Agents
In Backend/app/db/seed.py at line 12 and also lines 20 and 43-45, plain text
passwords are being stored directly in the password_hash field, which is a
critical security risk. To fix this, replace the plain text assignment with code
that hashes the password using a secure hashing function (e.g., bcrypt or
hashlib with salt) before storing it. Ensure all password assignments in these
lines are updated to store only hashed passwords.

"role": "creator",
"bio": "Lifestyle and travel content creator",
},
{
"id": "6dbfcdd5-795f-49c1-8f7a-a5538b8c6f6f",
"username": "brand1",
"email": "[email protected]",
"password": "password123",
"role": "brand",
"bio": "Sustainable fashion brand looking for influencers",
},
]

# 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()
Comment on lines +30 to +33
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use ORM query methods instead of raw SQL.

The code is using raw SQL table select instead of the proper ORM query methods, which is less maintainable and type-safe.

-            existing_user = await session.execute(
-                User.__table__.select().where(User.email == user_data["email"])
-            )
-            existing_user = existing_user.scalar_one_or_none()
+            from sqlalchemy import select
+            stmt = select(User).where(User.email == user_data["email"])
+            result = await session.execute(stmt)
+            existing_user = result.scalar_one_or_none()
🤖 Prompt for AI Agents
In Backend/app/db/seed.py around lines 30 to 33, replace the raw SQL table
select query with the proper ORM query method by using
session.query(User).filter(User.email == user_data["email"]).one_or_none() to
fetch the existing user. This change improves maintainability and type safety by
leveraging the ORM's query interface instead of direct table selects.


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

# Commit the session
await session.commit()
print("✅ Users seeded successfully.")
23 changes: 21 additions & 2 deletions Backend/app/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from db.db import engine
from models import models
from db.seed import seed_db
from models import models, chat
from routes.post import router as post_router
from routes.chat import router as chat_router
from sqlalchemy.exc import SQLAlchemyError
import logging
import os
Expand All @@ -11,28 +14,44 @@
# Load environment variables
load_dotenv()


# Async function to create database tables with exception handling
async def create_tables():
try:
async with engine.begin() as conn:
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:
print(f"❌ Error creating tables: {e}")

#Lifespan context manager for startup and shutdown events

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


# Initialize FastAPI
app = FastAPI(lifespan=lifespan)

# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

# Include the routes
app.include_router(post_router)
app.include_router(chat_router)


@app.get("/")
async def home():
Expand Down
54 changes: 54 additions & 0 deletions Backend/app/models/chat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from sqlalchemy import Column, String, ForeignKey, DateTime, Enum, UniqueConstraint
from sqlalchemy.orm import relationship
from datetime import datetime, timezone
from db.db import Base
import uuid
import enum


def generate_uuid():
return str(uuid.uuid4())

Comment on lines +9 to +11
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

Remove duplicate generate_uuid function.

This function is already defined in models.py. Import it instead to avoid code duplication.

Replace the duplicate function with an import:

-def generate_uuid():
-    return str(uuid.uuid4())
+from models.models import generate_uuid
📝 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
def generate_uuid():
return str(uuid.uuid4())
-from uuid import uuid4
-
-def generate_uuid():
- return str(uuid4())
+from models.models import generate_uuid
🤖 Prompt for AI Agents
In Backend/app/models/chat.py around lines 9 to 11, there is a duplicate
definition of the generate_uuid function which already exists in models.py.
Remove this local function definition and instead import generate_uuid from
models.py at the top of the file. This will eliminate code duplication and
maintain a single source of truth for the function.


class MessageStatus(enum.Enum):
SENT = "sent"
DELIVERED = "delivered"
SEEN = "seen"


class ChatList(Base):
__tablename__ = "chat_list"

id = Column(String, primary_key=True, default=generate_uuid)
user1_id = Column(String, ForeignKey("users.id"), nullable=False)
user2_id = Column(String, ForeignKey("users.id"), nullable=False)
last_message_time = Column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)

user1 = relationship("User", foreign_keys=[user1_id], backref="chatlist_user1")
user2 = relationship("User", foreign_keys=[user2_id], backref="chatlist_user2")

__table_args__ = (UniqueConstraint("user1_id", "user2_id", name="unique_chat"),)


class ChatMessage(Base):
__tablename__ = "chat_messages"

id = Column(String, primary_key=True, default=generate_uuid)
sender_id = Column(String, ForeignKey("users.id"), nullable=False)
receiver_id = Column(String, ForeignKey("users.id"), nullable=False)
message = Column(String, nullable=False)
status = Column(
Enum(MessageStatus), default=MessageStatus.SENT
) # Using the enum class
created_at = Column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)

sender = relationship("User", foreign_keys=[sender_id], backref="sent_messages")
receiver = relationship(
"User", foreign_keys=[receiver_id], backref="received_messages"
)
chat_list_id = Column(String, ForeignKey("chat_list.id"), nullable=False)
chat = relationship("ChatList", backref="messages")
72 changes: 61 additions & 11 deletions Backend/app/models/models.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
from sqlalchemy import Column, String, Integer, ForeignKey, Float, Text, JSON, DECIMAL, TIMESTAMP
from sqlalchemy import (
Column,
String,
Integer,
ForeignKey,
Float,
Text,
JSON,
DECIMAL,
DateTime,
Boolean,
)
from sqlalchemy.orm import relationship
from datetime import datetime, timezone
from db.db import Base
import uuid


def generate_uuid():
return str(uuid.uuid4())


# User Table (Creators & Brands)
class User(Base):
__tablename__ = "users"
Expand All @@ -18,13 +31,30 @@ class User(Base):
role = Column(String, nullable=False) # 'creator' or 'brand'
profile_image = Column(Text, nullable=True)
bio = Column(Text, nullable=True)
created_at = Column(TIMESTAMP, default=lambda: datetime.now(timezone.utc))
created_at = Column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)

is_online = Column(Boolean, default=False) # ✅ Track if user is online
last_seen = Column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)

audience = relationship("AudienceInsights", back_populates="user", uselist=False)
sponsorships = relationship("Sponsorship", back_populates="brand")
posts = relationship("UserPost", back_populates="user")
applications = relationship("SponsorshipApplication", back_populates="creator")
payments = relationship("SponsorshipPayment", back_populates="creator")
payments = relationship(
"SponsorshipPayment",
foreign_keys="[SponsorshipPayment.creator_id]",
back_populates="creator",
)
brand_payments = relationship(
"SponsorshipPayment",
foreign_keys="[SponsorshipPayment.brand_id]",
back_populates="brand",
)


# Audience Insights Table
class AudienceInsights(Base):
Expand All @@ -38,10 +68,13 @@ class AudienceInsights(Base):
average_views = Column(Integer)
time_of_attention = Column(Integer) # in seconds
price_expectation = Column(DECIMAL(10, 2))
created_at = Column(TIMESTAMP, default=lambda: datetime.now(timezone.utc))
created_at = Column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)

user = relationship("User", back_populates="audience")


# Sponsorship Table (For Brands)
class Sponsorship(Base):
__tablename__ = "sponsorships"
Expand All @@ -54,11 +87,14 @@ class Sponsorship(Base):
budget = Column(DECIMAL(10, 2))
engagement_minimum = Column(Float)
status = Column(String, default="open")
created_at = Column(TIMESTAMP, default=lambda: datetime.now(timezone.utc))
created_at = Column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)

brand = relationship("User", back_populates="sponsorships")
applications = relationship("SponsorshipApplication", back_populates="sponsorship")


# User Posts Table
class UserPost(Base):
__tablename__ = "user_posts"
Expand All @@ -70,10 +106,13 @@ class UserPost(Base):
post_url = Column(Text, nullable=True)
category = Column(String, nullable=True)
engagement_metrics = Column(JSON) # {"likes": 500, "comments": 100, "shares": 50}
created_at = Column(TIMESTAMP, default=lambda: datetime.now(timezone.utc))
created_at = Column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)

user = relationship("User", back_populates="posts")


# Sponsorship Applications Table
class SponsorshipApplication(Base):
__tablename__ = "sponsorship_applications"
Expand All @@ -84,11 +123,14 @@ class SponsorshipApplication(Base):
post_id = Column(String, ForeignKey("user_posts.id"), nullable=True)
proposal = Column(Text, nullable=False)
status = Column(String, default="pending")
applied_at = Column(TIMESTAMP, default=datetime.utcnow)
applied_at = Column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)

creator = relationship("User", back_populates="applications")
sponsorship = relationship("Sponsorship", back_populates="applications")


# Collaborations Table
class Collaboration(Base):
__tablename__ = "collaborations"
Expand All @@ -98,7 +140,10 @@ class Collaboration(Base):
creator_2_id = Column(String, ForeignKey("users.id"), nullable=False)
collaboration_details = Column(Text, nullable=False)
status = Column(String, default="pending")
created_at = Column(TIMESTAMP,default=lambda: datetime.now(timezone.utc))
created_at = Column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)


# Sponsorship Payments Table
class SponsorshipPayment(Base):
Expand All @@ -110,6 +155,11 @@ class SponsorshipPayment(Base):
sponsorship_id = Column(String, ForeignKey("sponsorships.id"), nullable=False)
amount = Column(DECIMAL(10, 2), nullable=False)
status = Column(String, default="pending")
transaction_date = Column(TIMESTAMP, default=datetime.utcnow)

creator = relationship("User", back_populates="payments")
transaction_date = Column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)

creator = relationship("User", foreign_keys=[creator_id], back_populates="payments")
brand = relationship(
"User", foreign_keys=[brand_id], back_populates="brand_payments"
)
Loading