Skip to content

Commit

Permalink
Merge pull request #5 from Chefies/feat/auth-profile-picture
Browse files Browse the repository at this point in the history
Feat/auth profile picture
  • Loading branch information
rorre authored Jun 11, 2024
2 parents 5266425 + f9f8c0e commit a5e80ee
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 9 deletions.
3 changes: 2 additions & 1 deletion cefies/app.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from fastapi import FastAPI

import cefies.internal.firestore # noqa: F401 -- Intended to initialize Firebase
from cefies.routes import index_router, auth_router
from cefies.routes import index_router, auth_router, profile_router

app = FastAPI(root_path="/api")
app.include_router(index_router)
app.include_router(auth_router)
app.include_router(profile_router)
Empty file added cefies/models/forms/__init__.py
Empty file.
16 changes: 16 additions & 0 deletions cefies/models/forms/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from fastapi import Form, UploadFile, File

from cefies.models.forms.base import BaseForm

class RegisterForm(BaseForm):
def __init__(
self,
email: str = Form(...),
name: str = Form(...),
password: str = Form(...),
avatar: UploadFile = File(...),
):
self.email = email
self.name = name
self.password = password
self.avatar = avatar
3 changes: 3 additions & 0 deletions cefies/models/forms/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class BaseForm:
def to_dict(self):
return self.__dict__.copy()
11 changes: 11 additions & 0 deletions cefies/models/profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from typing import Annotated
from pydantic import BaseModel, StringConstraints


class ProfileData(BaseModel):
email: str
name: str
avatar: str

class ChangePasswordData(BaseModel):
password: Annotated[str, StringConstraints(min_length=8)]
3 changes: 2 additions & 1 deletion cefies/routes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from cefies.routes.index import router as index_router
from cefies.routes.auth import router as auth_router

from cefies.routes.profile import router as profile_router

__all__ = [
"index_router",
"auth_router",
"profile_router",
]
32 changes: 25 additions & 7 deletions cefies/routes/auth.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from typing import Optional
from fastapi import APIRouter, HTTPException, UploadFile, status
import asyncio
from fastapi import APIRouter, HTTPException, status, Depends
from pydantic import ValidationError

from cefies.models.forms.auth import RegisterForm
from cefies.models.auth import LoginData, RegisterData, Token
from cefies.models.db.user import User
from cefies.models.response import MessageResponse
from cefies.security import authenticate_user, create_access_token, get_password_hash
from cefies.security import authenticate_user, create_access_token, get_password_hash, get_hash_sha256
from cefies.internal import bucket


router = APIRouter(prefix="/auth")
Expand All @@ -25,9 +28,20 @@ async def login(data: LoginData):


@router.post("/register")
def register(data: RegisterData):
async def register(form: RegisterForm = Depends()):
loop = asyncio.get_running_loop()

try:
data_dict = form.to_dict()
data_dict.pop("avatar", None)
data = RegisterData(**data_dict)
except ValidationError as e:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=e.errors(),
)

existing_user = User.collection.filter(email=data.email).get()
print(existing_user)
if existing_user:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
Expand All @@ -38,8 +52,12 @@ def register(data: RegisterData):
new_user.email = data.email
new_user.name = data.name
new_user.password = get_password_hash(data.password)
# TODO: Upload file to object and set avatar
new_user.avatar = ""
avatar_content = await form.avatar.read()
avatar_url = await loop.run_in_executor(
None,
lambda: bucket.upload_file(avatar_content, get_hash_sha256(avatar_content))
)
new_user.avatar = avatar_url
new_user.save()

return MessageResponse(
Expand Down
26 changes: 26 additions & 0 deletions cefies/routes/profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from typing import Annotated
from fastapi import APIRouter, Depends
from fastapi.responses import JSONResponse

from cefies.models.db.user import User
from cefies.models.profile import ProfileData, ChangePasswordData
from cefies.security import get_current_user, get_password_hash


router = APIRouter(prefix="/profile")


@router.get("/")
def get_profile(
user: Annotated[User, Depends(get_current_user)],
):
return ProfileData(**user.to_dict())

@router.patch("/password")
def change_password(
user: Annotated[User, Depends(get_current_user)],
data: ChangePasswordData,
):
user.password = get_password_hash(data.password)
user.save()
return JSONResponse(content={"detail": "password changed"}, status_code=200)
5 changes: 5 additions & 0 deletions cefies/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
import bcrypt
import hashlib

from cefies.models.db.user import User
from datetime import datetime, timedelta, timezone
Expand Down Expand Up @@ -43,6 +44,10 @@ def get_password_hash(password: str):
return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()


def get_hash_sha256(content: bytes):
return hashlib.sha256(content).hexdigest()


def get_current_user(token: Annotated[str, Depends(security)]):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
Expand Down

0 comments on commit a5e80ee

Please sign in to comment.