Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions app/auth/jwt_handler.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from datetime import timedelta, datetime

import jwt
import os
import dotenv
Expand All @@ -10,6 +12,11 @@
SECRET_KEY = os.getenv("SECRET_KEY")
ALGORITHM = "HS256"

async def create_access_token(subject: dict, expires_delta: timedelta = timedelta(minutes=5)):
to_encode = subject.copy()
to_encode.update({"exp": datetime.utcnow() + expires_delta})


async def get_current_user(request: Request):
token = request.headers.get("Authorization")
if not token or not token.startswith("Bearer "):
Expand Down
10 changes: 5 additions & 5 deletions app/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@

async def init_db_pool():
db_pool = await asyncpg.create_pool(
user=os.getenv(""),
password=os.getenv(""),
database=os.getenv(""),
host=os.getenv(""),
port=os.getenv(""),
user=os.getenv("DB_USER"),
password=os.getenv("DB_PASSWORD"),
database=os.getenv("DB_NAME"),
host=os.getenv("DB_HOST"),
port=os.getenv("DB_PORT"),
min_size=2, # minimum number of connections
max_size=10, # maximum number of connections
)
Expand Down
203 changes: 120 additions & 83 deletions app/services/farmer_service.py
Original file line number Diff line number Diff line change
@@ -1,99 +1,136 @@
# farmer service business logic
import asyncpg
import uuid
from uuid import uuid4
from datetime import datetime
from fastapi import HTTPException, status
from app.db.connection import get_db_connection
from app.auth.jwt_handler import create_access_token
from app.auth.password_utils import hash_password, validate_password_strength, verify_password
from app.utils.token import create_access_token
from app.auth.password_utils import (
hash_password,
validate_password_strength,
verify_password
)
import logging

logger = logging.getLogger(__name__)

class FarmerService:
def __init__(self):
self.conn = get_db_connection()
self.cursor = self.conn.cursor()
def __init__(self, db_pool: asyncpg.pool.Pool):
self.db_pool = db_pool

def register_farmer(self, email: str, password: str, username: str, role: str):
# Generate farmer_id
async def register_farmer(self, email: str, password: str, username: str, role: str):
farmer_id = str(uuid.uuid4())[:8]
hashed_password = hash_password(password)

# Check password strength
if not validate_password_strength(password):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Password strength is too weak"
)

# Check if the farmer already exists in the database
self.cursor.execute("SELECT id FROM farmers WHERE email = %s AND username = %s", (email, username))
if self.cursor.fetchone():
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Farmer with this email or username already exists"
)

try:
# Insert the farmer into the database
self.cursor.execute(
"INSERT INTO farmers (id, username, email, password, role)",
(farmer_id, username, email, hashed_password, role)
)
farmer_id = self.cursor.fetchone()[0]
self.conn.commit()

return {"message": "Farmer registered successfully", "farmer_id": farmer_id}

except Exception as e:
self.conn.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"An error occurred while registering the user: {str(e)}"
)
finally:
self._cleanup()


def add_products(self, farmer_id, product_name, product_category, quantity, date=datetime.now()):
# method to add a product(s) to the database
# product must have a product id
product_id = str(uuid.uuid4())[:5]

#check if product already exists, if yes then only add its quantity
self.cursor.execute("SELECT product_id FROM products WHERE farmer_id = %s", (farmer_id,))
if self.cursor.fetchone():
return {"message": "This product already exists!"}


#def add_product(self, farmer_id, product_name, product_category, quantity, date=datetime.now):
# # method to add a product(s) to the database
# # product must have a product id
# try:
# connection = get_db_connection()
# cursor = connection.cursor()
#
# connection.autocommit = False
#
# self.cursor.execute(
# "INSERT INTO products (farmer_id, product_name, product_category, quantity, date)",
# (farmer_id, product_name, product_category, quantity, date)
# )
# self.connection.commit()
# except Exception as e:
# self.conn.rollback()
# raise HTTPException(
# status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# details=f"Error adding products to the database! {str(e)}"
# )
#
# finally:
# self._cleanup()




def _cleanup(self):
if self.cursor:
self.cursor.close()
if self.conn:
self.conn.close()
async with self.db_pool.acquire() as conn:
try:
existing = await conn.fetchrow(
"""
SELECT id FROM farmers
WHERE email = $1 OR username = $2
""",
email,
username
)

if existing:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Farmer with this email or username already exists"
)

await conn.execute(
"""
INSERT INTO farmers (id, username, email, password, role)
VALUES ($1, $2, $3, $4, $5)
""",
farmer_id,
username,
email,
hashed_password,
role
)

return {"message": "Farmer registered successfully", "farmer_id": farmer_id}

except HTTPException:
raise

except Exception as e:
logger.error(
f"Farmer Registration Request: Error occurred while registering farmer: {e}"
)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="An error occurred while registering the user."
)

async def get_farmer_by_email(self, email: str):
async with self.db_pool.acquire() as conn:
try:
farmer = await conn.fetchrow(
"SELECT * FROM farmers WHERE email = $1",
email
)

if not farmer:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Farmer with this email does not exist"
)

return dict(farmer)

except HTTPException:
raise

except Exception as e:
logger.error(
f"Farmer Fetch Request: Database error fetching farmer '{email}': {e}"
)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="An error occurred while getting the farmer."
)

async def farmer_login(self, email: str, password: str):
async with self.db_pool.acquire() as conn:
try:
farmer = await conn.fetchrow(
"SELECT id, password FROM farmers WHERE email = $1",
email
)

if not farmer:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Farmer with this email does not exist"
)

farmer_id = farmer["id"]
hashed_password = farmer["password"]

if not verify_password(password, hashed_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect password"
)

token = create_access_token({"farmer_id": farmer_id})
return {"access_token": token}

except HTTPException:
raise

except Exception as e:
logger.error(
f"Farmer Login Request: Error occurred during login: {e}"
)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="An error occurred while trying to log in."
)
Loading
Loading