Fix login errors, add get /users and get /user (username or id) routes.

This commit is contained in:
Hannah dagemark 2026-09-09 17:08:28 +02:00
commit fbf731acb6
5 changed files with 59 additions and 24 deletions

View file

@ -1,3 +1,5 @@
# Initial Commit :D # Initial Commit :D
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?) # 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?)

View file

@ -6,12 +6,17 @@ from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer from fastapi.security import OAuth2PasswordBearer
from pwdlib import PasswordHash from pwdlib import PasswordHash
from app.config import Settings from app.config import settings
from app.database import add_to_database, get_from_database_where 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.token import TokenData
from app.models.user import User, UserIn, UserInDB from app.models.user import User, UserIn, UserInDB
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/users/auth")
password_hash = PasswordHash.recommended() password_hash = PasswordHash.recommended()
@ -26,25 +31,37 @@ def get_password_hash(password: str):
return password_hash.hash(password) return password_hash.hash(password)
def get_users():
return get_from_database(UserInDB)
def get_user(username: str): def get_user(username: str):
user = get_from_database_where( user = get_from_database_where(
User, UserInDB,
User.username, # type: ignore (Type correct on runtime) UserInDB.username, # type: ignore (Type correct on runtime)
username, username,
True, limit=1,
) )
if user is not None: 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): def save_user(user: UserIn):
print("Got save user call")
user_in_db = UserInDB( user_in_db = UserInDB(
username=user.username, username=user.username,
authority=user.authority, authority=user.authority,
hashed_password=get_password_hash(user.password), hashed_password=get_password_hash(user.password),
) )
add_to_database(user_in_db) 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): def authenticate_user(username: str, password: str):
@ -69,8 +86,8 @@ def create_access_token(
to_encode.update({"exp": expire}) to_encode.update({"exp": expire})
encoded_jwt = jwt.encode( # pyright: ignore[reportUnknownMemberType] (Third party skill issue) encoded_jwt = jwt.encode( # pyright: ignore[reportUnknownMemberType] (Third party skill issue)
to_encode, to_encode,
Settings.secret_key, settings.secret_key,
algorithm=Settings.algorithm, algorithm=settings.algorithm,
) )
return encoded_jwt return encoded_jwt
@ -84,8 +101,8 @@ async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
try: try:
payload = jwt.decode( # pyright: ignore[reportUnknownMemberType] (Third party skill issue) payload = jwt.decode( # pyright: ignore[reportUnknownMemberType] (Third party skill issue)
token, token,
Settings.secret_key, settings.secret_key,
algorithms=[Settings.algorithm], algorithms=[settings.algorithm],
) )
username = payload.get("sub") username = payload.get("sub")
if username is None: 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! user = get_user(token_data.username) # type: ignore Look just above here idiot!
if user is None: if user is None:
raise credentials_exception raise credentials_exception
return user return User.model_validate(user)
async def get_current_active_user( async def get_current_active_user(

View file

@ -4,7 +4,7 @@ from fastapi import FastAPI
from app.config import settings from app.config import settings
from app.database import create_database 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 @asynccontextmanager
@ -24,3 +24,4 @@ app = FastAPI(
app.include_router(project_route.router) app.include_router(project_route.router)
app.include_router(service_route.router) app.include_router(service_route.router)
app.include_router(user_route.router)

View file

@ -1,7 +1,7 @@
from sqlmodel import Field, SQLModel from sqlmodel import Field, SQLModel
class User(SQLModel, table=True): class User(SQLModel):
id: int | None = Field(default=None, primary_key=True) id: int | None = Field(default=None, primary_key=True)
username: str username: str
@ -13,5 +13,5 @@ class UserIn(User):
password: str password: str
class UserInDB(User): class UserInDB(User, table=True):
hashed_password: str hashed_password: str

View file

@ -4,11 +4,13 @@ from typing import Annotated
from fastapi import APIRouter, Depends, HTTPException, status from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordRequestForm
from app.config import Settings from app.config import settings
from app.logic.user_logic import ( from app.logic.user_logic import (
authenticate_user, authenticate_user,
create_access_token, create_access_token,
get_current_user, get_current_user,
get_user_by_id,
get_users,
save_user, save_user,
) )
from app.models.token import Token from app.models.token import Token
@ -18,14 +20,17 @@ router = APIRouter(prefix="/users", tags=["users"])
@router.get("/") @router.get("/")
def get_default(): def fetch_users():
return {"message": "Root for Users API"} users = get_users()
if not users:
return None
return [User.model_validate(user) for user in users]
@router.post("/") @router.post("/")
async def create_user(user_in: UserIn): async def create_user(user_in: UserIn):
user = save_user(user_in) 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( access_token = create_access_token(
data={"sub": user.username}, data={"sub": user.username},
expires_delta=access_token_expires, 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") return Token(access_token=access_token, token_type="bearer")
@router.get("/me") @router.get("/user")
async def read_me(current_user: Annotated[User, Depends(get_current_user)]): async def fetch_current_user(current_user: Annotated[User, Depends(get_current_user)]):
return 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( async def login_for_access_token(
form_data: Annotated[OAuth2PasswordRequestForm, Depends()], form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
) -> Token: ) -> Token:
@ -49,7 +64,7 @@ async def login_for_access_token(
detail="Incorrect username or password", detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"}, 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( access_token = create_access_token(
data={"sub": user.username}, data={"sub": user.username},
expires_delta=access_token_expires, expires_delta=access_token_expires,