Skip to content
Open
Show file tree
Hide file tree
Changes from 15 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
25 changes: 25 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Virtual environment
venv/

# Python cache files
__pycache__/
*.pyc
*.pyo
*.pyd

# Logs & temp files
*.log
*.tmp
*.swp
*.swo

# IDE/Editor files
.vscode/
.idea/

# Environment variables (if needed)
.env

# OS-specific files
.DS_Store
Thumbs.db
12 changes: 12 additions & 0 deletions backend/app/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from fastapi import FastAPI
from app.routes import auth, donor, organisation

app = FastAPI()
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add CORS middleware and API versioning.

To support frontend integration and API versioning:

  1. Add CORS middleware to handle cross-origin requests
  2. Implement API versioning for better maintainability

Apply this diff:

 from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
 from app.routes import auth, donor, organisation

-app = FastAPI()
+app = FastAPI(
+    title="Donor Management API",
+    version="1.0.0",
+)
+
+# Configure CORS
+app.add_middleware(
+    CORSMiddleware,
+    allow_origins=["*"],  # In production, replace with specific origins
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"],
+)
📝 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
from fastapi import FastAPI
from app.routes import auth, donor, organisation
app = FastAPI()
from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
from app.routes import auth, donor, organisation
-app = FastAPI()
+app = FastAPI(
+ title="Donor Management API",
+ version="1.0.0",
+)
+
+# Configure CORS
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["*"], # In production, replace with specific origins
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+)


app.include_router(auth.router, prefix="/auth", tags=["Auth"])
app.include_router(donor.router, prefix="/donor", tags=["Donor"])
app.include_router(organisation.router, prefix="/organisation", tags=["Organisation"])

@app.get("/")
def home():
return {"message": "Welcome to the FastAPI Backend (No Database)"}
28 changes: 28 additions & 0 deletions backend/app/routes/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from passlib.context import CryptContext

router = APIRouter()
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

users = [] # In-memory storage

class UserCreate(BaseModel):
name: str
email: str
password: str
Comment on lines +23 to +26
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add input validation for user fields.

The current model lacks validation for email format and password strength.

Apply this diff to add validation:

+from pydantic import BaseModel, EmailStr, Field
+import re
+
+def validate_password(v: str) -> str:
+    if not re.match(r'^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$', v):
+        raise ValueError('Password must be at least 8 characters long and contain both letters and numbers')
+    return v
+
 class UserCreate(BaseModel):
     name: str
-    email: str
-    password: str
+    email: EmailStr
+    password: str = Field(..., min_length=8)
+
+    @validator('password')
+    def password_validation(cls, v):
+        return validate_password(v)

Committable suggestion skipped: line range outside the PR's diff.


@router.post("/register")
def register(user: UserCreate):
for u in users:
if u["email"] == user.email:
raise HTTPException(status_code=400, detail="Email already exists")

hashed_password = pwd_context.hash(user.password)
new_user = {"name": user.name, "email": user.email, "password": hashed_password}
users.append(new_user)
return {"message": "User registered successfully"}

@router.post("/login")
def login():
return {"message": "Login endpoint (To be implemented)"}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Implement login endpoint with JWT authentication.

The login endpoint is currently a placeholder. Implement proper authentication using JWT tokens.

Implement JWT authentication:

+from datetime import datetime, timedelta
+from jose import JWTError, jwt
+from fastapi.security import OAuth2PasswordBearer
+
+SECRET_KEY = "your-secret-key"  # Move to environment variables
+ALGORITHM = "HS256"
+ACCESS_TOKEN_EXPIRE_MINUTES = 30
+
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
+
 @router.post("/login")
-def login():
-    return {"message": "Login endpoint (To be implemented)"}
+async def login(email: str, password: str):
+    user = next((u for u in users if u["email"] == email), None)
+    if not user or not pwd_context.verify(password, user["password"]):
+        raise HTTPException(
+            status_code=401,
+            detail="Incorrect email or password",
+            headers={"WWW-Authenticate": "Bearer"},
+        )
+    
+    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
+    access_token = create_access_token(
+        data={"sub": user["email"]}, expires_delta=access_token_expires
+    )
+    return {"access_token": access_token, "token_type": "bearer"}
+
+def create_access_token(data: dict, expires_delta: timedelta):
+    to_encode = data.copy()
+    expire = datetime.utcnow() + expires_delta
+    to_encode.update({"exp": expire})
+    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
+    return encoded_jwt
📝 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
@router.post("/login")
def login():
return {"message": "Login endpoint (To be implemented)"}
from datetime import datetime, timedelta
from jose import JWTError, jwt
from fastapi.security import OAuth2PasswordBearer
SECRET_KEY = "your-secret-key" # Move to environment variables
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@router.post("/login")
async def login(email: str, password: str):
user = next((u for u in users if u["email"] == email), None)
if not user or not pwd_context.verify(password, user["password"]):
raise HTTPException(
status_code=401,
detail="Incorrect email or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user["email"]}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
def create_access_token(data: dict, expires_delta: timedelta):
to_encode = data.copy()
expire = datetime.utcnow() + expires_delta
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt

15 changes: 15 additions & 0 deletions backend/app/routes/donor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from fastapi import APIRouter
from pydantic import BaseModel

router = APIRouter()
donors = [] # In-memory storage

class DonorCreate(BaseModel):
name: str
age: int
contact: str
Comment on lines +7 to +10
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add input validation for age and contact fields.

The current model lacks validation constraints. Consider adding:

  • Age range validation
  • Contact number format validation

Apply this diff to add validation:

 class DonorCreate(BaseModel):
     name: str
-    age: int
-    contact: str
+    age: int = Field(..., gt=0, lt=150)
+    contact: str = Field(..., regex=r'^\+?1?\d{9,15}$')
📝 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
class DonorCreate(BaseModel):
name: str
age: int
contact: str
class DonorCreate(BaseModel):
name: str
age: int = Field(..., gt=0, lt=150)
contact: str = Field(..., regex=r'^\+?1?\d{9,15}$')


@router.post("/register")
def register_donor(donor: DonorCreate):
donors.append(donor.dict())
return {"message": "Donor registered successfully", "donor": donor}
Comment on lines +12 to +15
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling and implement GET endpoint.

The current implementation lacks error handling and a way to retrieve donor information.

Add error handling and a GET endpoint:

+@router.get("/")
+def get_donors():
+    return {"donors": donors}
+
 @router.post("/register")
-def register_donor(donor: DonorCreate):
+async def register_donor(donor: DonorCreate):
+    try:
         donors.append(donor.dict())
         return {"message": "Donor registered successfully", "donor": donor}
+    except Exception as e:
+        raise HTTPException(status_code=500, detail=str(e))
📝 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
@router.post("/register")
def register_donor(donor: DonorCreate):
donors.append(donor.dict())
return {"message": "Donor registered successfully", "donor": donor}
@router.get("/")
def get_donors():
return {"donors": donors}
@router.post("/register")
async def register_donor(donor: DonorCreate):
try:
donors.append(donor.dict())
return {"message": "Donor registered successfully", "donor": donor}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

19 changes: 19 additions & 0 deletions backend/app/routes/organisation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from fastapi import APIRouter
from pydantic import BaseModel

router = APIRouter()
organisations = [] # In-memory storage

class OrganisationCreate(BaseModel):
name: str
description: str
contact: str
Comment on lines +7 to +10
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add input validation for organisation fields.

The current model lacks validation constraints for contact and description fields.

Apply this diff to add validation:

 class OrganisationCreate(BaseModel):
-    name: str
-    description: str
-    contact: str
+    name: str = Field(..., min_length=1, max_length=100)
+    description: str = Field(..., min_length=10, max_length=1000)
+    contact: str = Field(..., regex=r'^\+?1?\d{9,15}$')
📝 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
class OrganisationCreate(BaseModel):
name: str
description: str
contact: str
class OrganisationCreate(BaseModel):
name: str = Field(..., min_length=1, max_length=100)
description: str = Field(..., min_length=10, max_length=1000)
contact: str = Field(..., regex=r'^\+?1?\d{9,15}$')


@router.post("/register")
def register_organisation(org: OrganisationCreate):
organisations.append(org.dict())
return {"message": "Organisation registered successfully", "organisation": org}

@router.get("/")
def get_organisations():
return {"organisations": organisations}
Comment on lines +12 to +19
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling and CRUD operations.

The current implementation lacks error handling and complete CRUD operations.

Add error handling and additional endpoints:

+from fastapi import HTTPException
+
 @router.post("/register")
-def register_organisation(org: OrganisationCreate):
+async def register_organisation(org: OrganisationCreate):
+    try:
         organisations.append(org.dict())
         return {"message": "Organisation registered successfully", "organisation": org}
+    except Exception as e:
+        raise HTTPException(status_code=500, detail=str(e))

 @router.get("/")
-def get_organisations():
+async def get_organisations():
     return {"organisations": organisations}
+
+@router.put("/{org_id}")
+async def update_organisation(org_id: int, org: OrganisationCreate):
+    if org_id >= len(organisations):
+        raise HTTPException(status_code=404, detail="Organisation not found")
+    organisations[org_id] = org.dict()
+    return {"message": "Organisation updated successfully"}
+
+@router.delete("/{org_id}")
+async def delete_organisation(org_id: int):
+    if org_id >= len(organisations):
+        raise HTTPException(status_code=404, detail="Organisation not found")
+    organisations.pop(org_id)
+    return {"message": "Organisation deleted successfully"}
📝 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
@router.post("/register")
def register_organisation(org: OrganisationCreate):
organisations.append(org.dict())
return {"message": "Organisation registered successfully", "organisation": org}
@router.get("/")
def get_organisations():
return {"organisations": organisations}
from fastapi import HTTPException
@router.post("/register")
async def register_organisation(org: OrganisationCreate):
try:
organisations.append(org.dict())
return {"message": "Organisation registered successfully", "organisation": org}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/")
async def get_organisations():
return {"organisations": organisations}
@router.put("/{org_id}")
async def update_organisation(org_id: int, org: OrganisationCreate):
if org_id >= len(organisations):
raise HTTPException(status_code=404, detail="Organisation not found")
organisations[org_id] = org.dict()
return {"message": "Organisation updated successfully"}
@router.delete("/{org_id}")
async def delete_organisation(org_id: int):
if org_id >= len(organisations):
raise HTTPException(status_code=404, detail="Organisation not found")
organisations.pop(org_id)
return {"message": "Organisation deleted successfully"}

23 changes: 23 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
70 changes: 70 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Getting Started with Create React App

This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).

## Available Scripts

In the project directory, you can run:

### `npm start`

Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.

The page will reload when you make changes.\
You may also see any lint errors in the console.

### `npm test`

Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.

### `npm run build`

Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.

The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!

See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.

### `npm run eject`

**Note: this is a one-way operation. Once you `eject`, you can't go back!**

If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.

Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.

You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.

## Learn More

You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).

To learn React, check out the [React documentation](https://reactjs.org/).

### Code Splitting

This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)

### Analyzing the Bundle Size

This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)

### Making a Progressive Web App

This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)

### Advanced Configuration

This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)

### Deployment

This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)

### `npm run build` fails to minify

This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
Loading