diff --git a/README.md b/README.md index 35fa759..d534972 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ # Initial Commit :D +<<<<<<< HEAD Run 'uv export --format requirements-txt --no-dev --no-emit-project --output-file requirements.txt' for major release versions (Maybe I make this a git actions thing?) Run uv run fastapi dev for dev environment -Recommended vscode extensions used for formatting: Ruff, Pylance. Use strict type checking. \ No newline at end of file +Recommended vscode extensions used for formatting: Ruff, Pylance. Use strict type checking. +======= +To run the devenvironment, run 'uv run fastapi dev' + +# Run 'uv export --format requirements-txt --no-dev --no-emit-project --output-file requirements.txt' for major release versions (Maybe I make this a git actions thing?) +>>>>>>> fbf731acb6404c94852d80d35ee16f2445eef414 diff --git a/app/logic/user_logic.py b/app/logic/user_logic.py index 206c0da..9e051d7 100644 --- a/app/logic/user_logic.py +++ b/app/logic/user_logic.py @@ -6,12 +6,17 @@ from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer from pwdlib import PasswordHash -from app.config import Settings -from app.database import add_to_database, get_from_database_where +from app.config import settings +from app.database import ( + add_to_database, + get_from_database, + get_from_database_where, + get_from_database_where_id, +) from app.models.token import TokenData from app.models.user import User, UserIn, UserInDB -oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") +oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/users/auth") password_hash = PasswordHash.recommended() @@ -26,25 +31,37 @@ def get_password_hash(password: str): return password_hash.hash(password) +def get_users(): + return get_from_database(UserInDB) + + def get_user(username: str): user = get_from_database_where( - User, - User.username, # type: ignore (Type correct on runtime) + UserInDB, + UserInDB.username, # type: ignore (Type correct on runtime) username, - True, + limit=1, ) if user is not None: - return UserInDB.model_validate(user[0]) + return user[0] + + +def get_user_by_id(id: int): + user = get_from_database_where_id(UserInDB, id) + if user is not None: + return user def save_user(user: UserIn): + print("Got save user call") user_in_db = UserInDB( username=user.username, authority=user.authority, hashed_password=get_password_hash(user.password), ) add_to_database(user_in_db) - return user_in_db + print("Saved user") + return User.model_validate(get_user(user.username)) def authenticate_user(username: str, password: str): @@ -69,8 +86,8 @@ def create_access_token( to_encode.update({"exp": expire}) encoded_jwt = jwt.encode( # pyright: ignore[reportUnknownMemberType] (Third party skill issue) to_encode, - Settings.secret_key, - algorithm=Settings.algorithm, + settings.secret_key, + algorithm=settings.algorithm, ) return encoded_jwt @@ -84,8 +101,8 @@ async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]): try: payload = jwt.decode( # pyright: ignore[reportUnknownMemberType] (Third party skill issue) token, - Settings.secret_key, - algorithms=[Settings.algorithm], + settings.secret_key, + algorithms=[settings.algorithm], ) username = payload.get("sub") if username is None: @@ -96,7 +113,7 @@ async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]): user = get_user(token_data.username) # type: ignore Look just above here idiot! if user is None: raise credentials_exception - return user + return User.model_validate(user) async def get_current_active_user( diff --git a/app/main.py b/app/main.py index 186eed0..3c6e7ae 100644 --- a/app/main.py +++ b/app/main.py @@ -4,7 +4,7 @@ from fastapi import FastAPI from app.config import settings from app.database import create_database -from app.routers import project_route, service_route +from app.routers import project_route, service_route, user_route @asynccontextmanager @@ -24,3 +24,4 @@ app = FastAPI( app.include_router(project_route.router) app.include_router(service_route.router) +app.include_router(user_route.router) diff --git a/app/models/user.py b/app/models/user.py index 234ba61..26f83ef 100644 --- a/app/models/user.py +++ b/app/models/user.py @@ -1,7 +1,7 @@ from sqlmodel import Field, SQLModel -class User(SQLModel, table=True): +class User(SQLModel): id: int | None = Field(default=None, primary_key=True) username: str @@ -13,5 +13,5 @@ class UserIn(User): password: str -class UserInDB(User): +class UserInDB(User, table=True): hashed_password: str diff --git a/app/routers/user_route.py b/app/routers/user_route.py index 1f37c38..775f007 100644 --- a/app/routers/user_route.py +++ b/app/routers/user_route.py @@ -4,11 +4,13 @@ from typing import Annotated from fastapi import APIRouter, Depends, HTTPException, status from fastapi.security import OAuth2PasswordRequestForm -from app.config import Settings +from app.config import settings from app.logic.user_logic import ( authenticate_user, create_access_token, get_current_user, + get_user_by_id, + get_users, save_user, ) from app.models.token import Token @@ -18,14 +20,17 @@ router = APIRouter(prefix="/users", tags=["users"]) @router.get("/") -def get_default(): - return {"message": "Root for Users API"} +def fetch_users(): + users = get_users() + if not users: + return None + return [User.model_validate(user) for user in users] @router.post("/") async def create_user(user_in: UserIn): user = save_user(user_in) - access_token_expires = timedelta(minutes=Settings.access_token_expire_minutes) + access_token_expires = timedelta(minutes=settings.access_token_expire_minutes) access_token = create_access_token( data={"sub": user.username}, expires_delta=access_token_expires, @@ -33,12 +38,22 @@ async def create_user(user_in: UserIn): return Token(access_token=access_token, token_type="bearer") -@router.get("/me") -async def read_me(current_user: Annotated[User, Depends(get_current_user)]): +@router.get("/user") +async def fetch_current_user(current_user: Annotated[User, Depends(get_current_user)]): return current_user -@router.post("/token") +@router.get("/user/id/{user_id}") +async def fetch_user_by_id(user_id: int): + return User.model_validate(get_user_by_id(user_id)) + + +@router.get("/user/name/{user_name}") +async def fetch_user_by_name(user_id: int): + return User.model_validate(get_user_by_id(user_id)) + + +@router.post("/auth") async def login_for_access_token( form_data: Annotated[OAuth2PasswordRequestForm, Depends()], ) -> Token: @@ -49,7 +64,7 @@ async def login_for_access_token( detail="Incorrect username or password", headers={"WWW-Authenticate": "Bearer"}, ) - access_token_expires = timedelta(minutes=Settings.access_token_expire_minutes) + access_token_expires = timedelta(minutes=settings.access_token_expire_minutes) access_token = create_access_token( data={"sub": user.username}, expires_delta=access_token_expires,