From f3e47126b34969256c79cf8efaf7e1c4deb90abf Mon Sep 17 00:00:00 2001 From: Thomas Peetz Date: Wed, 5 Nov 2025 21:48:25 +0100 Subject: [PATCH] add login functions for cookie and non-cookie authentication --- kontor-api/src/apis/utils.py | 48 ------ kontor-api/src/apis/version1/admin.py | 68 ++++---- kontor-api/src/apis/version1/comic.py | 2 +- kontor-api/src/apis/version1/mediaactor.py | 2 +- .../src/apis/version1/mediaactorfile.py | 2 +- kontor-api/src/apis/version1/mediafile.py | 2 +- kontor-api/src/apis/version1/tysc.py | 2 +- kontor-api/src/core/config.py | 2 +- kontor-api/src/core/security.py | 146 +++++++++++++++++- kontor-api/src/db/repository/admin.py | 4 +- kontor-api/src/db/repository/comics/artist.py | 17 +- kontor-api/src/db/session.py | 8 +- kontor-api/src/schema/admin.py | 15 +- kontor-api/src/schema/comics/artist.py | 4 + .../src/templates/comic/artist_edit.html | 4 +- kontor-api/src/webapps/admin/route_admin.py | 14 +- kontor-api/src/webapps/auth/route_login.py | 15 +- kontor-api/src/webapps/comic/forms/artist.py | 26 ++++ kontor-api/src/webapps/comic/route_artists.py | 16 +- kontor-api/src/webapps/comic/route_comics.py | 4 +- .../src/webapps/comic/route_worktype.py | 2 +- kontor-api/src/webapps/media/route_actors.py | 5 +- kontor-api/src/webapps/media/route_media.py | 9 +- kontor-api/src/webapps/media/route_videos.py | 4 +- 24 files changed, 279 insertions(+), 142 deletions(-) delete mode 100644 kontor-api/src/apis/utils.py create mode 100644 kontor-api/src/webapps/comic/forms/artist.py diff --git a/kontor-api/src/apis/utils.py b/kontor-api/src/apis/utils.py deleted file mode 100644 index 6d1ff22..0000000 --- a/kontor-api/src/apis/utils.py +++ /dev/null @@ -1,48 +0,0 @@ -from typing import Annotated -from typing import Dict -from typing import Optional - -from fastapi import HTTPException -from fastapi import Request -from fastapi import status -from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel -from fastapi.security import OAuth2 -from fastapi.security.utils import get_authorization_scheme_param - -from fastapi import Depends -from sqlalchemy.orm import Session - -from src.db.session import get_db - -SessionDep = Annotated[Session, Depends(get_db)] - - -class OAuth2PasswordBearerWithCookie(OAuth2): - def __init__( - self, - tokenUrl: str, - scheme_name: Optional[str] = None, - scopes: Optional[Dict[str, str]] = None, - auto_error: bool = True, - ): - if not scopes: - scopes = {} - flows = OAuthFlowsModel(password={"tokenUrl": tokenUrl, "scopes": scopes}) - super().__init__(flows=flows, scheme_name=scheme_name, auto_error=auto_error) - - async def __call__(self, request: Request) -> Optional[str]: - authorization: str = request.cookies.get( - "access_token" - ) # changed to accept access token from httpOnly Cookie - - scheme, param = get_authorization_scheme_param(authorization) - if not authorization or scheme.lower() != "bearer": - if self.auto_error: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Not authenticated", - headers={"WWW-Authenticate": "Bearer"}, - ) - else: - return None - return param diff --git a/kontor-api/src/apis/version1/admin.py b/kontor-api/src/apis/version1/admin.py index 4c7a626..215d565 100644 --- a/kontor-api/src/apis/version1/admin.py +++ b/kontor-api/src/apis/version1/admin.py @@ -1,35 +1,38 @@ -import logging from datetime import timedelta +from typing import Annotated -import bcrypt -from fastapi import APIRouter, HTTPException, status, Response, Depends +from fastapi import APIRouter, HTTPException, status, Depends, Response from fastapi.security import OAuth2PasswordRequestForm -from jose import jwt, JWTError -from src.apis.utils import SessionDep, OAuth2PasswordBearerWithCookie + from src.core.config import settings -from src.core.security import create_access_token +from src.core.security import create_access_token, authenticate_user, get_current_active_user from src.db.models.admin import Profile -from src.db.repository.admin import get_profile -from src.schema.admin import Token +from src.db.session import SessionDep +from src.schema.admin import Token, ProfileModel +from src.webapps.auth.forms import LoginForm router = APIRouter() -def authenticate_user(username: str, password: str, db: SessionDep) -> Profile | None: - user = get_profile(username=username, db=db) - print(user) +@router.post("/token") +def login_for_access_token(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]) -> Token: + user = authenticate_user(form_data.username, form_data.password) if not user: - return None - if bcrypt.checkpw(password.encode(), user.password.encode()): - print("User successful authenticated") - else: - logging.info("Authentication failed!") - return user + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Incorrect username or password", + headers={"WWW-Authenticate": "Bearer"}, + ) + access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) + access_token = create_access_token( + data={"sub": user.email, "scope": " ".join(form_data.scopes)}, expires_delta=access_token_expires + ) + return Token(access_token=access_token, token_type="bearer") -@router.post("/token", response_model=Token) -def login_for_access_token(response: Response, db: SessionDep, form_data: OAuth2PasswordRequestForm = Depends()): - user = authenticate_user(form_data.username, form_data.password, db) +# @router.post("/token-cookie", response_model=Token) +def login_for_token_cookie(response: Response, form_data: LoginForm = Depends()): + user = authenticate_user(form_data.username, form_data.password) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, @@ -45,25 +48,6 @@ def login_for_access_token(response: Response, db: SessionDep, form_data: OAuth2 return {"access_token": access_token, "token_type": "bearer"} -oauth2_scheme = OAuth2PasswordBearerWithCookie(tokenUrl="/api/login/token") - - -def get_current_user_from_token(db: SessionDep, token: str = Depends(oauth2_scheme)): - credentials_exception = HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Could not validate credentials", - ) - try: - payload = jwt.decode( - token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM] - ) - username: str = payload.get("sub") - print("username/email extracted is ", username) - if username is None: - raise credentials_exception - except JWTError: - raise credentials_exception - user = get_profile(username=username, db=db) - if user is None: - raise credentials_exception - return user +@router.get("/profile", response_model=ProfileModel) +async def read_profile(current_user: Annotated[Profile, Depends(get_current_active_user)]): + return current_user diff --git a/kontor-api/src/apis/version1/comic.py b/kontor-api/src/apis/version1/comic.py index 3d3446a..9a955f6 100644 --- a/kontor-api/src/apis/version1/comic.py +++ b/kontor-api/src/apis/version1/comic.py @@ -2,7 +2,6 @@ from typing import List from fastapi import APIRouter, HTTPException, status -from src.apis.utils import SessionDep from src.core.log_conf import logger from src.db.models.comic import Artist, Comic, Issue, Publisher from src.db.repository.comics.artist import get_artist_details @@ -13,6 +12,7 @@ from src.db.repository.comics.comic import ( list_comics, ) from src.db.repository.comics.publisher import get_publisher_details +from src.db.session import SessionDep from src.schema.comics.artist import ArtistCreation, ArtistResponse from src.schema.comics.artist_details import ArtistDetailResponse from src.schema.comics.comic import ComicResponse diff --git a/kontor-api/src/apis/version1/mediaactor.py b/kontor-api/src/apis/version1/mediaactor.py index 54a2fdf..d70baa5 100644 --- a/kontor-api/src/apis/version1/mediaactor.py +++ b/kontor-api/src/apis/version1/mediaactor.py @@ -1,8 +1,8 @@ from fastapi import APIRouter, status, HTTPException from sqlalchemy import select from src.core.log_conf import logger -from src.apis.utils import SessionDep from src.db.repository.media import create_new_mediaactor +from src.db.session import SessionDep from src.schema.media.actor import Actor, MediaActorResponse, get_actor_details from src.db.models.media import MediaActor diff --git a/kontor-api/src/apis/version1/mediaactorfile.py b/kontor-api/src/apis/version1/mediaactorfile.py index 3e8a890..0c0d657 100644 --- a/kontor-api/src/apis/version1/mediaactorfile.py +++ b/kontor-api/src/apis/version1/mediaactorfile.py @@ -1,8 +1,8 @@ from fastapi import APIRouter, status, HTTPException from sqlalchemy import select -from src.apis.utils import SessionDep from src.db.models.media import MediaActorFile from src.db.repository.media import delete_mediaactorfile +from src.db.session import SessionDep from src.schema.media.actorfile import MediaActorFileResponse, get_actorfile_details router = APIRouter() diff --git a/kontor-api/src/apis/version1/mediafile.py b/kontor-api/src/apis/version1/mediafile.py index 5955e9c..6840e0c 100644 --- a/kontor-api/src/apis/version1/mediafile.py +++ b/kontor-api/src/apis/version1/mediafile.py @@ -1,8 +1,8 @@ from fastapi import APIRouter, status, HTTPException, Depends from sqlalchemy import select, Sequence from src.core.log_conf import logger -from src.apis.utils import SessionDep from src.db.repository.media import create_new_mediaactorfile, create_new_mediafile +from src.db.session import SessionDep from src.schema.media.actor import MediaActorResponse from src.schema.media.actorfile import MediaActorFileResponse from src.schema.media.file import MediaFileResponse, Link, get_file_details, set_file diff --git a/kontor-api/src/apis/version1/tysc.py b/kontor-api/src/apis/version1/tysc.py index c8b8bb2..9d68288 100644 --- a/kontor-api/src/apis/version1/tysc.py +++ b/kontor-api/src/apis/version1/tysc.py @@ -1,7 +1,7 @@ from typing import List from fastapi import APIRouter -from src.apis.utils import SessionDep +from src.db.session import SessionDep from src.schema.tysc.sport import SportResponse from src.db.models.tysc import Sport diff --git a/kontor-api/src/core/config.py b/kontor-api/src/core/config.py index 1a2036a..47f52ba 100644 --- a/kontor-api/src/core/config.py +++ b/kontor-api/src/core/config.py @@ -19,7 +19,7 @@ class Settings: DATABASE_URL: str = f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_SERVER}:{DB_PORT}/{DB_DBNAME}" SECRET_KEY: str = os.getenv("SECRET_KEY", "J6GOtcwC2NJI1l0VkHu20PacPFGTxpirBxWwynoHjsc=") ALGORITHM = "HS256" - ACCESS_TOKEN_EXPIRE_MINUTES = 600 # in mins + ACCESS_TOKEN_EXPIRE_MINUTES = 60*24*7 # one week in mins settings = Settings() diff --git a/kontor-api/src/core/security.py b/kontor-api/src/core/security.py index 9d5c588..09f4741 100644 --- a/kontor-api/src/core/security.py +++ b/kontor-api/src/core/security.py @@ -1,17 +1,86 @@ -from datetime import datetime +import logging +from datetime import datetime, timezone from datetime import timedelta +from typing import Optional, Annotated, List +from typing import Dict from typing import Optional +from fastapi import HTTPException, Security +from fastapi import Request +from fastapi import status +from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel +from fastapi.security.utils import get_authorization_scheme_param + +import bcrypt +from fastapi import Depends +from fastapi.security import SecurityScopes, OAuth2PasswordBearer, OAuth2 +from pydantic import ValidationError + from src.core.config import settings -from jose import jwt +from jose import jwt, JWTError + +from src.core.log_conf import logger +from src.db.models.admin import Profile +from src.db.repository.admin import get_profile +from src.db.session import SessionLocal +from src.schema.admin import TokenData, ProfileModel + +oauth2_scheme = OAuth2PasswordBearer( + tokenUrl="/api/login/token", + scopes={"me": "read", "admin": "read"}, +) + + +class OAuth2PasswordBearerWithCookie(OAuth2): + def __init__( + self, + tokenUrl: str, + scheme_name: Optional[str] = None, + scopes: Optional[Dict[str, str]] = None, + auto_error: bool = True, + ): + if not scopes: + scopes = {} + flows = OAuthFlowsModel(password={"tokenUrl": tokenUrl, "scopes": scopes}) + super().__init__(flows=flows, scheme_name=scheme_name, auto_error=auto_error) + + async def __call__(self, request: Request) -> Optional[str]: + authorization: str = request.cookies.get( + "access_token" + ) # changed to accept access token from httpOnly Cookie + + scheme, param = get_authorization_scheme_param(authorization) + if not authorization or scheme.lower() != "bearer": + if self.auto_error: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Not authenticated", + headers={"WWW-Authenticate": "Bearer"}, + ) + else: + return None + return param + + +def authenticate_user(username: str, password: str) -> Optional[Profile]: + with SessionLocal() as db: + user = get_profile(username=username, db=db) + print(user) + if not user: + return None + if bcrypt.checkpw(password.encode(), user.password.encode()): + print("User successful authenticated") + else: + logger.info("Authentication failed!") + return user def create_access_token(data: dict, expires_delta: Optional[timedelta] = None): to_encode = data.copy() if expires_delta: - expire = datetime.utcnow() + expires_delta + expire = datetime.now(timezone.utc) + expires_delta else: - expire = datetime.utcnow() + timedelta( + expire = datetime.now(timezone.utc) + timedelta( minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES ) to_encode.update({"exp": expire}) @@ -19,3 +88,72 @@ def create_access_token(data: dict, expires_delta: Optional[timedelta] = None): to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM ) return encoded_jwt + + +async def get_current_user(security_scopes: SecurityScopes, token: Annotated[str, Depends(oauth2_scheme)]): + if security_scopes.scopes: + authenticate_value = f'Bearer scope="{security_scopes.scope_str}"' + else: + authenticate_value = "Bearer" + credentials_exception = HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Could not validate credentials", + headers={"WWW-Authenticate": authenticate_value}, + ) + try: + payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]) + username: str = payload.get("sub") + print("username/email extracted is ", username) + if username is None: + raise credentials_exception + scope: str = payload.get("scope", "") + token_scopes: List[str] = scope.split(" ") + token_data = TokenData(scopes=token_scopes, username=username) + except (JWTError, ValidationError): + raise credentials_exception + with SessionLocal() as db: + user = get_profile(username=token_data.username, db=db) + if user is None: + raise credentials_exception + for scope in security_scopes.scopes: + if scope not in token_scopes: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="not enough permissions", + headers={"WWW-Authenticate": authenticate_value}, + ) + return user + + +async def get_current_active_user( + current_user: Annotated[Profile, Security(get_current_user, scopes=["me"])], +) -> ProfileModel: + if not current_user.enabled: + raise HTTPException(status_code=400, detail="Inactive user") + user_model = ProfileModel(username=current_user.user_name, email=current_user.email, + first_name=current_user.first_name, last_name=current_user.last_name, + active=current_user.enabled) + return user_model + + + +def get_current_user_from_token(token: str = Depends(oauth2_scheme)): + credentials_exception = HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Could not validate credentials", + ) + try: + payload = jwt.decode( + token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM] + ) + username: str = payload.get("sub") + print("username/email extracted is ", username) + if username is None: + raise credentials_exception + except JWTError: + raise credentials_exception + with SessionLocal() as db: + user = get_profile(username=username, db=db) + if user is None: + raise credentials_exception + return user diff --git a/kontor-api/src/db/repository/admin.py b/kontor-api/src/db/repository/admin.py index a2f15e0..cc06404 100644 --- a/kontor-api/src/db/repository/admin.py +++ b/kontor-api/src/db/repository/admin.py @@ -1,10 +1,10 @@ -from typing import AnyStr +from typing import AnyStr, Optional from sqlalchemy.orm import Session from src.db.models.admin import Profile -def get_profile(username: AnyStr, db: Session): +def get_profile(username: AnyStr, db: Session) -> Optional[Profile]: profile = db.query(Profile).filter(Profile.email == username).first() return profile diff --git a/kontor-api/src/db/repository/comics/artist.py b/kontor-api/src/db/repository/comics/artist.py index b8b16c3..ec0077e 100644 --- a/kontor-api/src/db/repository/comics/artist.py +++ b/kontor-api/src/db/repository/comics/artist.py @@ -1,5 +1,11 @@ -from typing import List +import uuid +from typing import List, Optional + +from sqlalchemy.orm import Session + +from src.core.log_conf import logger from src.db.models.comic import Artist +from src.schema.comics.artist import AddArtist from src.schema.comics.artist_details import ArtistDetailResponse, ArtistWorktypeComicResponse, ArtistWorktypeIssueResponse from src.schema.comics.comic import ComicResponse from src.schema.comics.worktype import WorktypeResponse @@ -29,3 +35,12 @@ def get_artist_details(artist: Artist) -> ArtistDetailResponse: issue_works=issue_works, ) return response + +def update_artist(add_artist: AddArtist, artist_id: str, db: Session) -> Artist: + logger.info("update artist") + artist: Optional[Artist] = db.get(Artist, artist_id) + artist.name = add_artist.name + db.add(artist) + db.commit() + db.refresh(artist) + return artist diff --git a/kontor-api/src/db/session.py b/kontor-api/src/db/session.py index 38b40af..db8c1c0 100644 --- a/kontor-api/src/db/session.py +++ b/kontor-api/src/db/session.py @@ -1,7 +1,8 @@ -from typing import Generator +from typing import Generator, Annotated +from fastapi import Depends from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker +from sqlalchemy.orm import sessionmaker, Session from src.core.config import settings @@ -10,6 +11,9 @@ engine = create_engine(SQLALCHEMY_DATABASE_URL) SessionLocal = sessionmaker(bind=engine) + def get_db() -> Generator: with SessionLocal() as db: yield db + +SessionDep: type[Session] = Annotated[Session, Depends(get_db)] diff --git a/kontor-api/src/schema/admin.py b/kontor-api/src/schema/admin.py index 5efe689..f08eca6 100644 --- a/kontor-api/src/schema/admin.py +++ b/kontor-api/src/schema/admin.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, List from pydantic import BaseModel @@ -6,3 +6,16 @@ from pydantic import BaseModel class Token(BaseModel): access_token: str token_type: str + + +class TokenData(BaseModel): + username: Optional[str] = None + scopes: List[str] = [] + + +class ProfileModel(BaseModel): + username: str + email: str + first_name: str + last_name: str + active: bool diff --git a/kontor-api/src/schema/comics/artist.py b/kontor-api/src/schema/comics/artist.py index 4a967d4..e369737 100644 --- a/kontor-api/src/schema/comics/artist.py +++ b/kontor-api/src/schema/comics/artist.py @@ -8,3 +8,7 @@ class ArtistCreation(BaseModel): class ArtistResponse(BaseModel): id: str name: str + +class AddArtist(BaseModel): + id: str + name: str diff --git a/kontor-api/src/templates/comic/artist_edit.html b/kontor-api/src/templates/comic/artist_edit.html index c73b585..8348994 100644 --- a/kontor-api/src/templates/comic/artist_edit.html +++ b/kontor-api/src/templates/comic/artist_edit.html @@ -23,8 +23,8 @@
- - + +
diff --git a/kontor-api/src/webapps/admin/route_admin.py b/kontor-api/src/webapps/admin/route_admin.py index e2ab62d..0e7f611 100644 --- a/kontor-api/src/webapps/admin/route_admin.py +++ b/kontor-api/src/webapps/admin/route_admin.py @@ -1,31 +1,29 @@ -from typing import AnyStr +from typing import Optional from fastapi import APIRouter, Request -from fastapi.security.utils import get_authorization_scheme_param from fastapi.templating import Jinja2Templates -from src.apis.utils import SessionDep -from src.apis.version1.admin import get_current_user_from_token from src.db.models.admin import Permission, Profile +from src.db.session import SessionDep templates = Jinja2Templates(directory="src/templates") router = APIRouter(include_in_schema=False, prefix="/admin") @router.get("/profiles") -def get_profiles(db: SessionDep, request: Request, msg: str | None = None): +def get_profiles(db: SessionDep, request: Request, msg: Optional[str] = None): profiles = db.query(Profile).all() return templates.TemplateResponse("admin/profiles.html", {"request": request, "msg": msg, "profiles": profiles}) @router.get("/profiles/{profile_id}") -def comic_details(profile_id: AnyStr, request: Request, db: SessionDep): +def comic_details(profile_id: str, request: Request, db: SessionDep): profile = db.get(Profile, profile_id) return templates.TemplateResponse("admin/profile_detail.html", {"request": request, "profile":profile}) @router.get("/permissions") -def get_permissions(db: SessionDep, request: Request, msg: str | None = None): +def get_permissions(db: SessionDep, request: Request, msg: Optional[str] = None): permissions = db.query(Permission).all() return templates.TemplateResponse("admin/permissions.html", {"request": request, "msg": msg, "permissions": permissions}) @router.get("/permissions/{permission_id}") -def artist_detail(permission_id: AnyStr, request: Request, db: SessionDep): +def artist_detail(permission_id: str, request: Request, db: SessionDep): permission= db.get(Permission, str(permission_id)) return templates.TemplateResponse("comic/permission_detail.html", {"request": request, "permission": permission}) diff --git a/kontor-api/src/webapps/auth/route_login.py b/kontor-api/src/webapps/auth/route_login.py index 737cec7..a3c54e9 100644 --- a/kontor-api/src/webapps/auth/route_login.py +++ b/kontor-api/src/webapps/auth/route_login.py @@ -1,11 +1,12 @@ -from src.apis.version1.admin import login_for_access_token -from src.db.session import get_db -from fastapi import APIRouter -from fastapi import Depends +# from src.apis.version1.admin import login_for_access_token +from fastapi.security import OAuth2PasswordRequestForm + +from src.apis.version1.admin import login_for_token_cookie +from src.db.session import SessionDep +from fastapi import APIRouter, Depends from fastapi import HTTPException from fastapi import Request from fastapi.templating import Jinja2Templates -from sqlalchemy.orm import Session from src.webapps.auth.forms import LoginForm @@ -19,14 +20,14 @@ def login(request: Request): @router.post("/login/") -async def login(request: Request, db: Session = Depends(get_db)): +async def login(request: Request): form = LoginForm(request) await form.load_data() if await form.is_valid(): try: form.__dict__.update(msg="Login Successful :)") response = templates.TemplateResponse("auth/login.html", form.__dict__) - login_for_access_token(response=response, form_data=form, db=db) + login_for_token_cookie(response=response, form_data=form) return response except HTTPException: form.__dict__.update(msg="") diff --git a/kontor-api/src/webapps/comic/forms/artist.py b/kontor-api/src/webapps/comic/forms/artist.py new file mode 100644 index 0000000..a1df46a --- /dev/null +++ b/kontor-api/src/webapps/comic/forms/artist.py @@ -0,0 +1,26 @@ +from fastapi import Request +from typing import List, Optional + + +class AddArtistForm: + def __init__(self, request: Request, artist_id: str, artist_name: str, artist_link: str): + self.request = request + self.errors: List = [] + self.id: str = artist_id + self.name: Optional[str] = artist_name + self.link: Optional[str] = artist_link + + + async def load_data(self): + form = await self.request.form() + print(f"{form.keys()}") + self.name = form.get("artist.name") + self.link = form.get("artist.link") + + def is_valid(self): + if not self.errors: + return True + return False + + def __str__(self): + return f"{self.name=}, {self.link=}" diff --git a/kontor-api/src/webapps/comic/route_artists.py b/kontor-api/src/webapps/comic/route_artists.py index cbe2bcc..d6735d9 100644 --- a/kontor-api/src/webapps/comic/route_artists.py +++ b/kontor-api/src/webapps/comic/route_artists.py @@ -1,13 +1,17 @@ -from fastapi import APIRouter, Request, status +from fastapi import APIRouter, Request, status, Form from fastapi.templating import Jinja2Templates from fastapi.responses import RedirectResponse -from src.apis.utils import SessionDep from src.db.models.comic import Artist from typing import AnyStr +from src.db.repository.comics.artist import update_artist +from src.db.session import SessionDep #from src.db.repository.comic import create_new_worktype, update_worktype from src.main import logger +from src.schema.comics.artist import AddArtist +from src.webapps.comic.forms.artist import AddArtistForm + #from src.schema.comics.worktype import AddWorkType #from src.webapps.comic.forms import AddWorktypeForm @@ -31,13 +35,15 @@ def edit_artist(db: SessionDep, request: Request, artist_id: str): return templates.TemplateResponse("comic/artist_edit.html", {"request": request, "artist_name": artist.name, "artist_link": artist.weblink}) @router.post("/artist/edit/{artist_id}") -async def edit_artist(request: Request, db: SessionDep, artist_id: str): - form = AddArtistForm(request) +async def edit_artist(request: Request, db: SessionDep, artist_id: str, action: str = Form(...), artist_name: str = Form(...), artist_link: str = Form(...)): + if action == "cancel": + return RedirectResponse(f"/comic/artists/{artist_id}", status_code=status.HTTP_303_SEE_OTHER) + form = AddArtistForm(request, artist_id, artist_name, artist_link) await form.load_data() if form.is_valid(): try: artist = AddArtist(**form.__dict__) - artist = update_artist(artist=artist, artist_id=artist_id, db=db) + artist = update_artist(add_artist=artist, artist_id=artist_id, db=db) return RedirectResponse(f"/comic/artists/{artist.id}", status_code=status.HTTP_303_SEE_OTHER) except Exception as e: print(e) diff --git a/kontor-api/src/webapps/comic/route_comics.py b/kontor-api/src/webapps/comic/route_comics.py index 61511ad..6dd4725 100644 --- a/kontor-api/src/webapps/comic/route_comics.py +++ b/kontor-api/src/webapps/comic/route_comics.py @@ -2,11 +2,11 @@ from fastapi import APIRouter, Form, Request, status from fastapi.templating import Jinja2Templates from fastapi.responses import RedirectResponse -from src.apis.utils import SessionDep from src.db.models.comic import Comic, Publisher, Issue from typing import AnyStr from src.core.log_conf import logger from src.db.repository.comics.comic import update_comic +from src.db.session import SessionDep from src.schema.comics.comic import ComicSchema from src.webapps.comic.forms.comic import ValidateComicForm @@ -50,7 +50,7 @@ def edit_comic(db: SessionDep, request: Request, comic_id: str): @router.post("/comic/edit/{comic_id}") async def validate_comic(request: Request, db: SessionDep, comic_id: str, action: str = Form(...), completed: bool = Form(False), current_order: bool = Form(False)): if action == "cancel": - return RedirectResponse(f"/comic/comics/{comic_id}", status_code=status.HTTP_303_SEE_OTHER) + return RedirectResponse(f"/comic/comics/{comic_id}", status_code=status.HTTP_303_SEE_OTHER) form = ValidateComicForm(request, comic_id, completed, current_order) logger.info(f"request: {repr(request)}") await form.load_data() diff --git a/kontor-api/src/webapps/comic/route_worktype.py b/kontor-api/src/webapps/comic/route_worktype.py index d079564..365dc76 100644 --- a/kontor-api/src/webapps/comic/route_worktype.py +++ b/kontor-api/src/webapps/comic/route_worktype.py @@ -2,11 +2,11 @@ from fastapi import APIRouter, Request, status from fastapi.templating import Jinja2Templates from fastapi.responses import RedirectResponse -from src.apis.utils import SessionDep from src.db.models.comic import WorkType from typing import AnyStr from src.db.repository.comics.worktype import create_new_worktype, update_worktype +from src.db.session import SessionDep from src.main import logger from src.schema.comics.worktype import AddWorkType from src.webapps.comic.forms.worktype import AddWorktypeForm diff --git a/kontor-api/src/webapps/media/route_actors.py b/kontor-api/src/webapps/media/route_actors.py index 4b112b8..63bcce8 100644 --- a/kontor-api/src/webapps/media/route_actors.py +++ b/kontor-api/src/webapps/media/route_actors.py @@ -2,11 +2,8 @@ from typing import AnyStr from fastapi import APIRouter, Request from fastapi.templating import Jinja2Templates -from sqlalchemy import or_ - -from src.apis.utils import SessionDep from src.db.models.media import MediaActor -from src.core.log_conf import logger +from src.db.session import SessionDep templates = Jinja2Templates(directory="src/templates") router = APIRouter(include_in_schema=False, prefix="/media") diff --git a/kontor-api/src/webapps/media/route_media.py b/kontor-api/src/webapps/media/route_media.py index ef0f817..9b1e7a8 100644 --- a/kontor-api/src/webapps/media/route_media.py +++ b/kontor-api/src/webapps/media/route_media.py @@ -5,11 +5,11 @@ from fastapi.security.utils import get_authorization_scheme_param from fastapi.templating import Jinja2Templates from sqlalchemy import or_ -from src.apis.utils import SessionDep -from src.apis.version1.admin import get_current_user_from_token +from src.core.security import get_current_user_from_token from src.db.models.admin import Profile -from src.db.models.media import MediaFile, MediaActor +from src.db.models.media import MediaFile from src.core.log_conf import logger +from src.db.session import SessionDep templates = Jinja2Templates(directory="src/templates") router = APIRouter(include_in_schema=False, prefix="/media") @@ -37,7 +37,7 @@ def get_mediafiles(db: SessionDep, request: Request, msg: str | None = None): try: token = request.cookies.get("access_token") scheme, param = get_authorization_scheme_param(token) # scheme will hold "Bearer" and param will hold actual token value - current_user: Profile = get_current_user_from_token(token=param, db=db) + current_user: Profile = get_current_user_from_token(token=param) return templates.TemplateResponse("media/files.html", {"request": request, "msg": msg, "mediafiles": mediafiles}) except Exception as e: print(e) @@ -54,4 +54,3 @@ def file_details(file_id: AnyStr, request: Request, db: SessionDep): def edit_file(db: SessionDep, request: Request, file_id: str): media_file = db.get(MediaFile, file_id) return templates.TemplateResponse("media/file_detail.html", {"request": request, "mediafile":media_file}) - diff --git a/kontor-api/src/webapps/media/route_videos.py b/kontor-api/src/webapps/media/route_videos.py index b0b84b7..9c5c951 100644 --- a/kontor-api/src/webapps/media/route_videos.py +++ b/kontor-api/src/webapps/media/route_videos.py @@ -4,11 +4,11 @@ from fastapi import APIRouter, Request, status, responses from fastapi.security.utils import get_authorization_scheme_param from fastapi.templating import Jinja2Templates -from src.apis.utils import SessionDep from src.db.models.media import MediaVideo from src.db.repository.media import create_new_video -from src.apis.version1.admin import get_current_user_from_token +#from src.apis.version1.admin import get_current_user_from_token from src.db.models.admin import Profile +from src.db.session import SessionDep from src.schema.media.video import AddLink from src.webapps.media.forms import AddLinkForm