feat(users): add register route

pull/12/head
Cullen 2023-07-09 18:42:44 -05:00 committed by Cullen Watson
parent 3b9a491d89
commit 79db9578b4
11 changed files with 130 additions and 30 deletions

View File

@ -1,4 +1,5 @@
from fastapi import APIRouter from fastapi import APIRouter
from api.auth import router as auth_router
from .v1 import router as v1_router from .v1 import router as v1_router
router = APIRouter( router = APIRouter(
@ -6,3 +7,4 @@ router = APIRouter(
tags=["api"], tags=["api"],
) )
router.include_router(v1_router) router.include_router(v1_router)
router.include_router(auth_router)

8
api/auth/__init__.py Normal file
View File

@ -0,0 +1,8 @@
from fastapi import APIRouter
from api.auth.token import router as token_router
from api.auth.register import router as register_router
router = APIRouter(prefix="/auth")
router.include_router(token_router)
router.include_router(register_router)

View File

@ -6,10 +6,10 @@ from fastapi.security import OAuth2PasswordBearer
from settings import * from settings import *
from api.core.users import TokenData from api.core.users import TokenData
from .db_utils import UserInDB, get_user from api.auth.db_utils import UserInDB, get_user
load_dotenv() load_dotenv()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/token") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/token")
def create_access_token(data: dict): def create_access_token(data: dict):

View File

@ -1,25 +1,41 @@
from passlib.context import CryptContext from passlib.context import CryptContext
from supabase_py import create_client, Client from supabase_py import create_client, Client
from fastapi import HTTPException, status
from api.core.users import UserInDB from api.core.users import UserInDB
from settings import SUPABASE_URL, SUPABASE_KEY from settings import SUPABASE_URL, SUPABASE_KEY
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
def get_user(username: str):
result = supabase.table('users').select().eq('username', username).execute()
if 'error' in result and result['error']: def create_user(user_create: UserInDB):
result = supabase.table("users").insert(user_create.dict()).execute()
print(f"Insert result: {result}")
if "error" in result and result["error"]:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"User could not be created due to {result['error']['message']}",
)
return result
def get_user(username: str):
result = supabase.table("users").select().eq("username", username).execute()
if "error" in result and result["error"]:
print(f"Error: {result['error']['message']}") print(f"Error: {result['error']['message']}")
return None return None
else: else:
if result['data']: if result["data"]:
user_data = result['data'][0] # get the first (and should be only) user with the matching username user_data = result["data"][0]
return UserInDB(**user_data) return UserInDB(**user_data)
else: else:
return None return None
def verify_password(password: str, hashed_password: str): def verify_password(password: str, hashed_password: str):
return pwd_context.verify(password, hashed_password) return pwd_context.verify(password, hashed_password)

View File

@ -0,0 +1,28 @@
from fastapi import APIRouter, HTTPException, status
from api.core.users import UserCreate, UserInDB
from api.auth.db_utils import get_user, get_password_hash, create_user
router = APIRouter(prefix="/register", tags=["register"])
@router.post("/")
async def register_new_user(user: UserCreate):
existing_user = get_user(user.username)
if existing_user is not None:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Username already exists",
)
hashed_password = get_password_hash(user.password)
print(f"Hashed password: {hashed_password}")
user_create = UserInDB(
username=user.username,
email=user.email,
full_name=user.full_name,
hashed_password=hashed_password,
disabled=False,
)
create_user(user_create)
return {"detail": "User created successfully"}

View File

@ -2,17 +2,16 @@ from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordRequestForm
from api.core.users import Token from api.core.users import Token
from .db_utils import authenticate_user from api.auth.db_utils import authenticate_user
from .auth import create_access_token from api.auth.auth_utils import create_access_token
ACCESS_TOKEN_EXPIRE_MINUTES = 30 router = APIRouter(prefix="/token", tags=["token"])
router = APIRouter(prefix="/token")
@router.post("/", response_model=Token) @router.post("/", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()): async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(form_data.username, form_data.password) user = authenticate_user(form_data.username, form_data.password)
if user is None: if not user:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password", detail="Incorrect username or password",

View File

@ -1,10 +1,18 @@
from pydantic import BaseModel from pydantic import BaseModel
class User(BaseModel): class User(BaseModel):
username: str username: str
full_name: str full_name: str
email: str email: str
disabled: bool disabled: bool = False
class UserCreate(BaseModel):
username: str
full_name: str
email: str
password: str
class UserInDB(User): class UserInDB(User):

View File

@ -1,7 +1,8 @@
from fastapi import APIRouter from fastapi import APIRouter, Depends
from .jobs import router as jobs_router from .jobs import router as jobs_router
from .token import router as token_router from api.auth.token import router as token_router
from api.auth.auth_utils import get_active_current_user
router = APIRouter(prefix="/v1") router = APIRouter(prefix="/v1", dependencies=[Depends(get_active_current_user)])
router.include_router(jobs_router) router.include_router(jobs_router)
router.include_router(token_router) router.include_router(token_router)

View File

@ -4,9 +4,8 @@ from api.core.scrapers.indeed import IndeedScraper
from api.core.scrapers.ziprecruiter import ZipRecruiterScraper from api.core.scrapers.ziprecruiter import ZipRecruiterScraper
from api.core.scrapers.linkedin import LinkedInScraper from api.core.scrapers.linkedin import LinkedInScraper
from api.core.scrapers import ScraperInput, Site from api.core.scrapers import ScraperInput, Site
from ...v1.token.auth import get_active_current_user
router = APIRouter(prefix="/jobs", dependencies=[Depends(get_active_current_user)]) router = APIRouter(prefix="/jobs")
SCRAPER_MAPPING = { SCRAPER_MAPPING = {
Site.LINKEDIN: LinkedInScraper, Site.LINKEDIN: LinkedInScraper,

View File

@ -10,6 +10,7 @@ app = FastAPI(
) )
app.include_router(api_router) app.include_router(api_router)
@app.get("/", tags=["health"]) @app.get("/", tags=["health"])
async def health_check(): async def health_check():
return {"message": "JobSpy ready to scrape"} return {"message": "JobSpy ready to scrape"}

View File

@ -1,11 +1,49 @@
fastapi~=0.99.1 anyio==3.7.1
pydantic~=1.10.11 attrs==23.1.0
beautifulsoup4~=4.12.2 bcrypt==4.0.1
requests beautifulsoup4==4.12.2
pip~=21.3.1 certifi==2023.5.7
wheel~=0.37.1 cffi==1.15.1
setuptools~=60.2.0 chardet==4.0.0
passlib~=1.7.4 charset-normalizer==3.2.0
cryptography~=41.0.1 click==8.1.4
python-jose~=3.3.0 cryptography==41.0.1
python-dotenv~=1.0.0 dataclasses==0.6
deprecation==2.1.0
ecdsa==0.18.0
exceptiongroup==1.1.2
fastapi==0.99.1
gotrue==0.2.0
h11==0.14.0
httpcore==0.12.3
httpx==0.16.1
idna==2.10
iniconfig==2.0.0
packaging==23.1
passlib==1.7.4
pluggy==1.2.0
postgrest-py==0.4.0
py==1.11.0
pyasn1==0.5.0
pycparser==2.21
pydantic==1.10.11
pytest==6.2.5
python-dateutil==2.8.2
python-dotenv==1.0.0
python-jose==3.3.0
python-multipart==0.0.6
realtime-py==0.1.3
requests==2.25.1
rfc3986==1.5.0
rsa==4.9
six==1.16.0
sniffio==1.3.0
soupsieve==2.4.1
starlette==0.27.0
supabase-py==0.0.2
tls-client==0.2.1
toml==0.10.2
typing_extensions==4.7.1
urllib3==1.26.16
uvicorn==0.22.0
websockets==9.1