Remove obsolete endpoints (#89)
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Remove endpoints api/login/token and api/login/profile --------- Co-authored-by: Thomas Peetz <thomas.peetz@cimt-ag.de> Reviewed-on: #89
This commit was merged in pull request #89.
This commit is contained in:
@@ -3,20 +3,25 @@ add router for different parts (like comics, tysc, media)
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
from src.apis.version1.admin import token
|
from src.apis.version1.admin import mailaccount
|
||||||
from src.apis.version1.comics import artist, comic, issue
|
from src.apis.version1.comics import artist, comic, issue, worktype, volume, storyarc
|
||||||
from src.apis.version1.media import mediaactor, mediaactorfile, mediafile
|
from src.apis.version1.media import mediaactor, mediaactorfile, mediafile, mediavideo, mediaarticle
|
||||||
from src.apis.version1.tysc import card, cardset, fieldposition, player, rooster, sport, team, vendor
|
from src.apis.version1.tysc import card, cardset, fieldposition, player, rooster, sport, team, vendor
|
||||||
from src.core.security import get_current_user_from_token
|
from src.core.security import get_current_user_from_token
|
||||||
|
|
||||||
from src.apis.version1.user import profile
|
from src.apis.version1.user import assignment, permission, profile, token
|
||||||
from src.apis.version1.bookshelf import article
|
from src.apis.version1.bookshelf import article
|
||||||
|
|
||||||
api_router = APIRouter(prefix="/api")
|
api_router = APIRouter(prefix="/api")
|
||||||
api_router.include_router(comic.router, prefix="/comics", tags=["comics"], dependencies=[Depends(get_current_user_from_token)])
|
api_router.include_router(comic.router, prefix="/comics", tags=["comics"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
api_router.include_router(artist.router, prefix="/comics", tags=["comics"], dependencies=[Depends(get_current_user_from_token)])
|
api_router.include_router(artist.router, prefix="/comics", tags=["comics"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
api_router.include_router(issue.router, prefix="/comics", tags=["comics"], dependencies=[Depends(get_current_user_from_token)])
|
api_router.include_router(issue.router, prefix="/comics", tags=["comics"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
|
api_router.include_router(worktype.router, prefix="/comics", tags=["comics"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
|
api_router.include_router(volume.router, prefix="/comics", tags=["comics"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
|
api_router.include_router(storyarc.router, prefix="/comics", tags=["comics"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
api_router.include_router(mediafile.router, prefix="/media", tags=["media"], dependencies=[Depends(get_current_user_from_token)])
|
api_router.include_router(mediafile.router, prefix="/media", tags=["media"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
|
api_router.include_router(mediavideo.router, prefix="/media", tags=["media"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
|
api_router.include_router(mediaarticle.router, prefix="/media", tags=["media"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
api_router.include_router(mediaactor.router, prefix="/media", tags=["media"], dependencies=[Depends(get_current_user_from_token)])
|
api_router.include_router(mediaactor.router, prefix="/media", tags=["media"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
api_router.include_router(mediaactorfile.router, prefix="/media", tags=["media"], dependencies=[Depends(get_current_user_from_token)])
|
api_router.include_router(mediaactorfile.router, prefix="/media", tags=["media"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
api_router.include_router(sport.router, prefix="/tysc", tags=["tysc"], dependencies=[Depends(get_current_user_from_token)])
|
api_router.include_router(sport.router, prefix="/tysc", tags=["tysc"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
@@ -28,5 +33,8 @@ api_router.include_router(vendor.router, prefix="/tysc", tags=["tysc"], dependen
|
|||||||
api_router.include_router(cardset.router, prefix="/tysc", tags=["tysc"], dependencies=[Depends(get_current_user_from_token)])
|
api_router.include_router(cardset.router, prefix="/tysc", tags=["tysc"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
api_router.include_router(card.router, prefix="/tysc", tags=["tysc"], dependencies=[Depends(get_current_user_from_token)])
|
api_router.include_router(card.router, prefix="/tysc", tags=["tysc"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
api_router.include_router(article.router, prefix="/bookshelf", tags=["bookshelf"], dependencies=[Depends(get_current_user_from_token)])
|
api_router.include_router(article.router, prefix="/bookshelf", tags=["bookshelf"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
api_router.include_router(token.router, prefix="/login", tags=["login"])
|
|
||||||
api_router.include_router(profile.router, prefix="/user", tags=["user"], dependencies=[Depends(get_current_user_from_token)])
|
api_router.include_router(profile.router, prefix="/user", tags=["user"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
|
api_router.include_router(token.router, prefix="/user", tags=["user"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
|
api_router.include_router(permission.router, prefix="/user", tags=["user"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
|
api_router.include_router(assignment.router, prefix="/user", tags=["user"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
|
api_router.include_router(mailaccount.router, prefix="/admin", tags=["admin"], dependencies=[Depends(get_current_user_from_token)])
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, Response, status
|
||||||
from fastapi.security import OAuth2PasswordRequestForm
|
from fastapi.security import OAuth2PasswordRequestForm
|
||||||
from pydantic import BaseModel
|
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
from src.core.config import settings
|
from src.core.config import settings
|
||||||
from src.core.log_conf import logger
|
from src.core.log_conf import logger
|
||||||
@@ -11,16 +10,12 @@ from src.core.security import (
|
|||||||
authenticate_user_by_username,
|
authenticate_user_by_username,
|
||||||
create_access_token,
|
create_access_token,
|
||||||
)
|
)
|
||||||
from src.schema.admin import Token
|
from src.schema.admin.login import LoginRequest
|
||||||
|
from src.schema.admin.token import Token
|
||||||
|
from src.webapps.auth.forms import LoginForm
|
||||||
|
|
||||||
login_router = APIRouter()
|
login_router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
class LoginRequest(BaseModel):
|
|
||||||
email: str | None = None
|
|
||||||
password: str | None = None
|
|
||||||
|
|
||||||
|
|
||||||
@login_router.post(
|
@login_router.post(
|
||||||
"/login",
|
"/login",
|
||||||
tags=["login"],
|
tags=["login"],
|
||||||
@@ -47,9 +42,8 @@ def login(request: LoginRequest) -> Token:
|
|||||||
|
|
||||||
|
|
||||||
@login_router.post("/token", tags=["login"], summary="Login for access token")
|
@login_router.post("/token", tags=["login"], summary="Login for access token")
|
||||||
async def login_for_access_token(
|
#async def login_for_access_token(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]) -> Token:
|
||||||
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
|
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()) -> Token:
|
||||||
) -> Token:
|
|
||||||
user = authenticate_user_by_username(form_data.username, form_data.password)
|
user = authenticate_user_by_username(form_data.username, form_data.password)
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=400, detail="Incorrect username or password")
|
raise HTTPException(status_code=400, detail="Incorrect username or password")
|
||||||
@@ -59,3 +53,19 @@ async def login_for_access_token(
|
|||||||
expires_delta=access_token_expires,
|
expires_delta=access_token_expires,
|
||||||
)
|
)
|
||||||
return Token(access_token=access_token, token_type="bearer")
|
return Token(access_token=access_token, token_type="bearer")
|
||||||
|
|
||||||
|
def login_for_token_cookie(response: Response, form_data: LoginForm = Depends()):
|
||||||
|
user = authenticate_user_by_email(str(form_data.username), str(form_data.password))
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Incorrect username or password",
|
||||||
|
)
|
||||||
|
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||||
|
access_token = create_access_token(
|
||||||
|
data={"sub": user.email}, expires_delta=access_token_expires
|
||||||
|
)
|
||||||
|
response.set_cookie(
|
||||||
|
key="access_token", value=f"Bearer {access_token}", httponly=True
|
||||||
|
)
|
||||||
|
return {"access_token": access_token, "token_type": "bearer"}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from fastapi import APIRouter
|
|||||||
|
|
||||||
from src.db.models.admin import MailAccount
|
from src.db.models.admin import MailAccount
|
||||||
from src.db.session import SessionDep
|
from src.db.session import SessionDep
|
||||||
from src.schema.admin import MailAccountResponse, to_response
|
from src.schema.admin.mailaccount import MailAccountResponse, to_response
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
from datetime import timedelta
|
|
||||||
from typing import Annotated
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Body, HTTPException, status, Depends, Response
|
|
||||||
from fastapi.security import OAuth2PasswordRequestForm
|
|
||||||
|
|
||||||
from src.core.config import settings
|
|
||||||
from src.core.log_conf import logger
|
|
||||||
from src.core.security import create_access_token, authenticate_user_by_email, authenticate_user_by_username, get_current_active_user
|
|
||||||
from src.db.models.admin import Profile
|
|
||||||
from src.schema.admin import Token, ProfileModel
|
|
||||||
from src.webapps.auth.forms import LoginForm
|
|
||||||
|
|
||||||
router = APIRouter()
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/token")
|
|
||||||
def login_for_access_token(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]) -> Token:
|
|
||||||
user = authenticate_user_by_username(form_data.username, form_data.password)
|
|
||||||
logger.info(f"Request /token: login with {form_data.username}")
|
|
||||||
if not 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-cookie", response_model=Token)
|
|
||||||
def login_for_token_cookie(response: Response, form_data: LoginForm = Depends()):
|
|
||||||
user = authenticate_user_by_email(form_data.username, form_data.password) # type: ignore
|
|
||||||
if not user:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
||||||
detail="Incorrect username or password",
|
|
||||||
)
|
|
||||||
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
|
||||||
access_token = create_access_token(
|
|
||||||
data={"sub": user.email}, expires_delta=access_token_expires
|
|
||||||
)
|
|
||||||
response.set_cookie(
|
|
||||||
key="access_token", value=f"Bearer {access_token}", httponly=True
|
|
||||||
)
|
|
||||||
return {"access_token": access_token, "token_type": "bearer"}
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/profile", response_model=ProfileModel)
|
|
||||||
async def read_profile(current_user: Annotated[Profile, Depends(get_current_active_user)]):
|
|
||||||
return current_user
|
|
||||||
@@ -3,17 +3,17 @@ from typing import List
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from src.db.models.comic import Issue
|
from src.db.models.comic import Issue
|
||||||
from src.db.repository.comics.comic import get_issue_details
|
|
||||||
from src.db.session import SessionDep
|
from src.db.session import SessionDep
|
||||||
from src.schema.comics.issue_details import IssueDetailsResponse
|
from src.schema.comics.issue import IssueResponse, to_response
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/issues", response_model=List[IssueDetailsResponse])
|
@router.get("/issues", response_model=List[IssueResponse])
|
||||||
def get_issues(db: SessionDep) -> List[IssueDetailsResponse]:
|
def get_issues(db: SessionDep) -> List[IssueResponse]:
|
||||||
results: List[IssueDetailsResponse] = []
|
results: List[IssueResponse] = []
|
||||||
issues = db.query(Issue).all()
|
issues = db.query(Issue).all()
|
||||||
for issue in issues:
|
for issue in issues:
|
||||||
results.append(get_issue_details(issue))
|
response = to_response(issue)
|
||||||
|
results.append(response)
|
||||||
return results
|
return results
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from src.db.models.comic import StoryArc
|
||||||
|
from src.db.session import SessionDep
|
||||||
|
from src.schema.comics.storyarc import StoryArcResponse, to_response
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@router.get("/storyarcs", response_model=List[StoryArcResponse])
|
||||||
|
def get_issues(db: SessionDep) -> List[StoryArcResponse]:
|
||||||
|
results: List[StoryArcResponse] = []
|
||||||
|
storyarcs = db.query(StoryArc).all()
|
||||||
|
for storyarc in storyarcs:
|
||||||
|
response = to_response(storyarc)
|
||||||
|
results.append(response)
|
||||||
|
return results
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from src.db.models.comic import Volume
|
||||||
|
from src.db.session import SessionDep
|
||||||
|
from src.schema.comics.volume import VolumeResponse, to_response
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@router.get("/volumes", response_model=List[VolumeResponse])
|
||||||
|
def get_issues(db: SessionDep) -> List[VolumeResponse]:
|
||||||
|
results: List[VolumeResponse] = []
|
||||||
|
worktypes = db.query(Volume).all()
|
||||||
|
for worktype in worktypes:
|
||||||
|
response = to_response(worktype)
|
||||||
|
results.append(response)
|
||||||
|
return results
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from src.db.models.comic import WorkType
|
||||||
|
from src.db.session import SessionDep
|
||||||
|
from src.schema.comics.worktype import WorktypeResponse, to_response
|
||||||
|
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@router.get("/worktypes", response_model=List[WorktypeResponse])
|
||||||
|
def get_issues(db: SessionDep) -> List[WorktypeResponse]:
|
||||||
|
results: List[WorktypeResponse] = []
|
||||||
|
worktypes = db.query(WorkType).all()
|
||||||
|
for worktype in worktypes:
|
||||||
|
response = to_response(worktype)
|
||||||
|
results.append(response)
|
||||||
|
return results
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from fastapi import APIRouter, status
|
from fastapi import APIRouter, status
|
||||||
|
|
||||||
from src.schema.admin import HealthCheck
|
from src.schema.admin.healthcheck import HealthCheck
|
||||||
|
|
||||||
health_router = APIRouter()
|
health_router = APIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from src.db.models.media import MediaArticle
|
||||||
|
from src.db.session import SessionDep
|
||||||
|
from src.schema.media.article import MediaArticleResponse, to_response
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@router.get("/articles", response_model=List[MediaArticleResponse])
|
||||||
|
def get_all_files(
|
||||||
|
db: SessionDep, review: bool = False, download: bool = False
|
||||||
|
) -> List[MediaArticleResponse]:
|
||||||
|
"""
|
||||||
|
Get all MediaVideos.
|
||||||
|
"""
|
||||||
|
results: List[MediaArticleResponse] = []
|
||||||
|
articles: List[MediaArticle]
|
||||||
|
articles = db.query(MediaArticle).all()
|
||||||
|
for article in articles:
|
||||||
|
response = to_response(article)
|
||||||
|
results.append(response)
|
||||||
|
return results
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from src.db.models.media import MediaVideo
|
||||||
|
from src.db.session import SessionDep
|
||||||
|
from src.schema.media.video import MediaVideoResponse, to_response
|
||||||
|
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@router.get("/videos", response_model=List[MediaVideoResponse])
|
||||||
|
def get_all_files(
|
||||||
|
db: SessionDep, review: bool = False, download: bool = False
|
||||||
|
) -> List[MediaVideoResponse]:
|
||||||
|
"""
|
||||||
|
Get all MediaVideos.
|
||||||
|
"""
|
||||||
|
results: List[MediaVideoResponse] = []
|
||||||
|
files: List[MediaVideo]
|
||||||
|
if review:
|
||||||
|
files = db.query(MediaVideo).filter(MediaVideo.review.is_(True)).all()
|
||||||
|
elif download:
|
||||||
|
files = db.query(MediaVideo).filter(MediaVideo.should_download.is_(True)).all()
|
||||||
|
else:
|
||||||
|
files = db.query(MediaVideo).all()
|
||||||
|
for mediafile in files:
|
||||||
|
response = to_response(mediafile)
|
||||||
|
results.append(response)
|
||||||
|
return results
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from src.db.models.admin import Assignment
|
||||||
|
from src.db.session import SessionDep
|
||||||
|
from src.schema.user.assignment import AssignmentResponse, to_response
|
||||||
|
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/assignments", response_model=List[AssignmentResponse])
|
||||||
|
def get_all_profiles(db: SessionDep) -> List[AssignmentResponse]:
|
||||||
|
results: List[AssignmentResponse] = []
|
||||||
|
profiles = db.query(Assignment).all()
|
||||||
|
for profile in profiles:
|
||||||
|
response = to_response(profile)
|
||||||
|
results.append(response)
|
||||||
|
return results
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from src.db.models.admin import Permission
|
||||||
|
from src.db.session import SessionDep
|
||||||
|
from src.schema.user.permission import PermissionResponse, to_response
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/permissions", response_model=List[PermissionResponse])
|
||||||
|
def get_all_profiles(db: SessionDep) -> List[PermissionResponse]:
|
||||||
|
results: List[PermissionResponse] = []
|
||||||
|
permissions = db.query(Permission).all()
|
||||||
|
for permission in permissions:
|
||||||
|
response = to_response(permission)
|
||||||
|
results.append(response)
|
||||||
|
return results
|
||||||
@@ -3,6 +3,7 @@ from fastapi import APIRouter, HTTPException, status
|
|||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from src.core.log_conf import logger
|
from src.core.log_conf import logger
|
||||||
|
|
||||||
|
from src.core.security import CurrentUser
|
||||||
from src.db.models.admin import Profile
|
from src.db.models.admin import Profile
|
||||||
from src.db.repository.user import create_new_profile, get_profile_details
|
from src.db.repository.user import create_new_profile, get_profile_details
|
||||||
from src.db.session import SessionDep
|
from src.db.session import SessionDep
|
||||||
@@ -11,8 +12,13 @@ from src.schema.user.profile import ProfileResponse, ProfileModel
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/profile", response_model=ProfileModel)
|
||||||
|
async def read_profile(current_user: CurrentUser):
|
||||||
|
return current_user
|
||||||
|
|
||||||
@router.get("/profiles", response_model=List[ProfileResponse])
|
@router.get("/profiles", response_model=List[ProfileResponse])
|
||||||
def get_all_profiles(db: SessionDep) -> List[ProfileResponse]: # type: ignore
|
def get_all_profiles(db: SessionDep) -> List[ProfileResponse]:
|
||||||
results: List[ProfileResponse] = []
|
results: List[ProfileResponse] = []
|
||||||
profiles = db.scalars(select(Profile)).all()
|
profiles = db.scalars(select(Profile)).all()
|
||||||
for profile in profiles:
|
for profile in profiles:
|
||||||
@@ -21,7 +27,7 @@ def get_all_profiles(db: SessionDep) -> List[ProfileResponse]: # type: ignore
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
@router.get("/profiles/{profile_id}", response_model=ProfileResponse)
|
@router.get("/profiles/{profile_id}", response_model=ProfileResponse)
|
||||||
def get_profile(profile_id: str, db: SessionDep) -> ProfileResponse: # type: ignore
|
def get_profile(profile_id: str, db: SessionDep) -> ProfileResponse:
|
||||||
profile = db.get(Profile, profile_id)
|
profile = db.get(Profile, profile_id)
|
||||||
if not profile:
|
if not profile:
|
||||||
raise HTTPException(status_code=404, detail="MediaActor could not be found")
|
raise HTTPException(status_code=404, detail="MediaActor could not be found")
|
||||||
@@ -37,8 +43,8 @@ def delete_profile(profile_id: str, db: SessionDep): # type: ignore
|
|||||||
delete_profile(profile_id=profile_id, db=db)
|
delete_profile(profile_id=profile_id, db=db)
|
||||||
|
|
||||||
@router.post("/profiles", status_code=status.HTTP_201_CREATED)
|
@router.post("/profiles", status_code=status.HTTP_201_CREATED)
|
||||||
def add_profile(new_profile: ProfileModel, db: SessionDep) -> ProfileResponse: # type: ignore
|
def add_profile(new_profile: ProfileModel, db: SessionDep) -> ProfileResponse:
|
||||||
logger.info(f"add profile {new_profile.user_name}")
|
logger.info(f"add profile {new_profile.username}")
|
||||||
try:
|
try:
|
||||||
profile: Profile = create_new_profile(new_profile, db)
|
profile: Profile = create_new_profile(new_profile, db)
|
||||||
except:
|
except:
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from src.db.models.admin import Token
|
||||||
|
from src.db.session import SessionDep
|
||||||
|
from src.schema.user.token import TokenResponse, to_response
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/tokens", response_model=List[TokenResponse])
|
||||||
|
def get_all_profiles(db: SessionDep) -> List[TokenResponse]:
|
||||||
|
results: List[TokenResponse] = []
|
||||||
|
tokens = db.query(Token).all()
|
||||||
|
for token in tokens:
|
||||||
|
response = to_response(token)
|
||||||
|
results.append(response)
|
||||||
|
return results
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
|
from fastapi import Request, Response
|
||||||
|
from starlette.middleware.base import BaseHTTPMiddleware
|
||||||
|
from src.core.log_conf import logger
|
||||||
|
|
||||||
|
class RequestLoggingMiddleware(BaseHTTPMiddleware):
|
||||||
|
async def dispatch(self, request: Request, call_next) -> Response:
|
||||||
|
start_time = time.time()
|
||||||
|
path = request.url.path
|
||||||
|
|
||||||
|
if path != "/health":
|
||||||
|
# Log request info
|
||||||
|
request_info ={
|
||||||
|
"method": request.method,
|
||||||
|
"path": path,
|
||||||
|
"client_ip": request.client.host if request.client else "unknown"
|
||||||
|
}
|
||||||
|
logger.info("Incoming: %s", json.dumps(request_info))
|
||||||
|
|
||||||
|
# Process request
|
||||||
|
response = await call_next(request)
|
||||||
|
|
||||||
|
if path != "/health":
|
||||||
|
# Log response info
|
||||||
|
duration_ms = (time.time()- start_time)*1000
|
||||||
|
response_info = {
|
||||||
|
"status_code": response.status_code,
|
||||||
|
"duration_ms": round(duration_ms, 2)
|
||||||
|
}
|
||||||
|
logger.info("completed: %s", json.dumps(response_info))
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
import logging
|
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Annotated, Dict, List, Optional
|
from typing import Annotated, List, Optional
|
||||||
|
|
||||||
import bcrypt
|
import bcrypt
|
||||||
from fastapi import Depends, HTTPException, Request, Security, status
|
from fastapi import Depends, HTTPException, Security, status
|
||||||
from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel
|
#from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel
|
||||||
from fastapi.security import OAuth2, OAuth2PasswordBearer, SecurityScopes
|
from fastapi.security import (
|
||||||
from fastapi.security.utils import get_authorization_scheme_param
|
#OAuth2,
|
||||||
|
OAuth2PasswordBearer,
|
||||||
|
SecurityScopes
|
||||||
|
)
|
||||||
|
#from fastapi.security.utils import get_authorization_scheme_param
|
||||||
from jose import JWTError, jwt
|
from jose import JWTError, jwt
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
|
|
||||||
@@ -19,7 +22,8 @@ from src.db.repository.admin import (
|
|||||||
is_database_empty,
|
is_database_empty,
|
||||||
)
|
)
|
||||||
from src.db.session import SessionLocal
|
from src.db.session import SessionLocal
|
||||||
from src.schema.admin import ProfileModel, TokenData
|
from src.schema.admin.token import TokenData
|
||||||
|
from src.schema.user.profile import ProfileModel, to_model
|
||||||
|
|
||||||
oauth2_scheme = OAuth2PasswordBearer(
|
oauth2_scheme = OAuth2PasswordBearer(
|
||||||
tokenUrl="/token",
|
tokenUrl="/token",
|
||||||
@@ -161,13 +165,7 @@ async def get_current_active_user(
|
|||||||
) -> ProfileModel:
|
) -> ProfileModel:
|
||||||
if not current_user.enabled:
|
if not current_user.enabled:
|
||||||
raise HTTPException(status_code=400, detail="Inactive user")
|
raise HTTPException(status_code=400, detail="Inactive user")
|
||||||
user_model = ProfileModel(
|
user_model = to_model(current_user)
|
||||||
username=current_user.user_name,
|
|
||||||
email=current_user.email, # type: ignore
|
|
||||||
first_name=current_user.first_name,
|
|
||||||
last_name=current_user.last_name, # type: ignore
|
|
||||||
active=current_user.enabled,
|
|
||||||
) # type: ignore
|
|
||||||
return user_model
|
return user_model
|
||||||
|
|
||||||
|
|
||||||
@@ -181,7 +179,7 @@ def get_current_user_from_token(token: str = Depends(oauth2_scheme)):
|
|||||||
payload = jwt.decode(
|
payload = jwt.decode(
|
||||||
token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]
|
token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]
|
||||||
)
|
)
|
||||||
username: str = payload.get("sub") # type: ignore
|
username: Optional[str] = payload.get("sub")
|
||||||
logger.info("username/email extracted is %s", username)
|
logger.info("username/email extracted is %s", username)
|
||||||
if username is None:
|
if username is None:
|
||||||
raise credentials_exception
|
raise credentials_exception
|
||||||
@@ -190,8 +188,12 @@ def get_current_user_from_token(token: str = Depends(oauth2_scheme)):
|
|||||||
with SessionLocal() as db:
|
with SessionLocal() as db:
|
||||||
user = get_profile_by_email(email=username, db=db)
|
user = get_profile_by_email(email=username, db=db)
|
||||||
if user is None:
|
if user is None:
|
||||||
raise credentials_exception
|
user = get_profile_by_username(username=username, db=db)
|
||||||
|
if user is None:
|
||||||
|
raise credentials_exception
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
UserDep = Annotated[Profile, Depends(get_current_user_from_token)]
|
UserDep = Annotated[Profile, Depends(get_current_user_from_token)]
|
||||||
|
|
||||||
|
CurrentUser = Annotated[Profile, Depends(get_current_active_user)]
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ def delete_profile(db: Session, profile_id: str):
|
|||||||
def get_profile_details(profile: Profile) -> ProfileResponse:
|
def get_profile_details(profile: Profile) -> ProfileResponse:
|
||||||
reponse: ProfileResponse = ProfileResponse(
|
reponse: ProfileResponse = ProfileResponse(
|
||||||
id=profile.id,
|
id=profile.id,
|
||||||
user_name=str(profile.user_name)
|
username=str(profile.user_name)
|
||||||
)
|
)
|
||||||
return reponse
|
return reponse
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from src.apis.version1.healthcheck import health_router
|
|||||||
from src.apis.version1.admin.login import login_router
|
from src.apis.version1.admin.login import login_router
|
||||||
from src.core.config import settings
|
from src.core.config import settings
|
||||||
from src.core.log_conf import logger
|
from src.core.log_conf import logger
|
||||||
|
from src.core.middleware import RequestLoggingMiddleware
|
||||||
from src.db.models.base import Base
|
from src.db.models.base import Base
|
||||||
from src.db.session import engine
|
from src.db.session import engine
|
||||||
from src.db.utils import check_db_connected, check_db_disconnected
|
from src.db.utils import check_db_connected, check_db_disconnected
|
||||||
@@ -23,10 +24,10 @@ async def lifespan(app: FastAPI):
|
|||||||
|
|
||||||
|
|
||||||
def include_router(app: FastAPI):
|
def include_router(app: FastAPI):
|
||||||
app.include_router(api_router)
|
app.include_router(api_router, tags=["api"])
|
||||||
app.include_router(web_app_router)
|
app.include_router(web_app_router, tags=["webapp"])
|
||||||
app.include_router(health_router)
|
app.include_router(health_router, tags=["admin"])
|
||||||
app.include_router(login_router)
|
app.include_router(login_router, tags=["webapp"])
|
||||||
|
|
||||||
|
|
||||||
def configure_static(app: FastAPI):
|
def configure_static(app: FastAPI):
|
||||||
@@ -41,6 +42,7 @@ def add_middle_ware(app: FastAPI):
|
|||||||
allow_methods=["*"],
|
allow_methods=["*"],
|
||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
)
|
)
|
||||||
|
app.add_middleware(RequestLoggingMiddleware)
|
||||||
|
|
||||||
|
|
||||||
def create_tables():
|
def create_tables():
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class HealthCheck(BaseModel):
|
||||||
|
"""
|
||||||
|
Health check model
|
||||||
|
"""
|
||||||
|
|
||||||
|
status: str = "ok"
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class LoginRequest(BaseModel):
|
||||||
|
email: Optional[str] = None
|
||||||
|
password: Optional[str] = None
|
||||||
@@ -1,37 +1,10 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Optional, List
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from src.db.models.admin import MailAccount
|
from src.db.models.admin import MailAccount
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
class HealthCheck(BaseModel):
|
|
||||||
"""
|
|
||||||
Health check model
|
|
||||||
"""
|
|
||||||
|
|
||||||
status: str = "ok"
|
|
||||||
|
|
||||||
|
|
||||||
class MailAccountResponse(BaseModel):
|
class MailAccountResponse(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
created_date: datetime
|
created_date: datetime
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class Token(BaseModel):
|
||||||
|
access_token: str
|
||||||
|
token_type: str
|
||||||
|
|
||||||
|
|
||||||
|
class TokenData(BaseModel):
|
||||||
|
username: Optional[str] = None
|
||||||
|
scopes: List[str] = []
|
||||||
@@ -1,8 +1,38 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from src.db.models.comic import Issue
|
||||||
|
|
||||||
|
|
||||||
class IssueResponse(BaseModel):
|
class IssueResponse(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
|
created_date: datetime
|
||||||
|
last_modified_date: datetime
|
||||||
|
version: int
|
||||||
issue_number: str
|
issue_number: str
|
||||||
|
title: Optional[str]
|
||||||
|
published_on: Optional[datetime]
|
||||||
in_stock: bool
|
in_stock: bool
|
||||||
is_read: bool
|
is_read: bool
|
||||||
|
comic_id: str
|
||||||
|
volume_id: Optional[str]
|
||||||
|
story_arc_id: Optional[str]
|
||||||
|
|
||||||
|
def to_response(issue: Issue) -> IssueResponse:
|
||||||
|
response: IssueResponse = IssueResponse(
|
||||||
|
id=issue.id,
|
||||||
|
created_date=issue.created_date,
|
||||||
|
last_modified_date=issue.last_modified_date,
|
||||||
|
version=issue.version,
|
||||||
|
issue_number=issue.issue_number,
|
||||||
|
title=issue.title,
|
||||||
|
published_on=issue.published_on,
|
||||||
|
in_stock=issue.in_stock,
|
||||||
|
is_read=issue.is_read,
|
||||||
|
comic_id=issue.comic_id,
|
||||||
|
volume_id=issue.volume_id,
|
||||||
|
story_arc_id=issue.story_arc_id
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|||||||
@@ -11,3 +11,4 @@ class IssueDetailsResponse(BaseModel):
|
|||||||
is_read: bool
|
is_read: bool
|
||||||
comic: ComicResponse
|
comic: ComicResponse
|
||||||
volume: VolumeResponse | None
|
volume: VolumeResponse | None
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from src.db.models.comic import StoryArc
|
||||||
|
|
||||||
|
class StoryArcResponse(BaseModel):
|
||||||
|
id: str
|
||||||
|
created_date: datetime
|
||||||
|
last_modified_date: datetime
|
||||||
|
version: int
|
||||||
|
name: str
|
||||||
|
comic_id: str
|
||||||
|
volume_id: Optional[str]
|
||||||
|
|
||||||
|
class AddLink(BaseModel):
|
||||||
|
url: str
|
||||||
|
|
||||||
|
def to_response(storyarc: StoryArc) -> StoryArcResponse:
|
||||||
|
response: StoryArcResponse = StoryArcResponse(
|
||||||
|
id=storyarc.id,
|
||||||
|
created_date=storyarc.created_date,
|
||||||
|
last_modified_date=storyarc.last_modified_date,
|
||||||
|
version=storyarc.version,
|
||||||
|
name=storyarc.name,
|
||||||
|
comic_id=storyarc.comic_id,
|
||||||
|
volume_id=storyarc.volume_id
|
||||||
|
)
|
||||||
|
return response
|
||||||
@@ -1,6 +1,25 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from src.db.models.comic import Volume
|
||||||
|
|
||||||
|
|
||||||
class VolumeResponse(BaseModel):
|
class VolumeResponse(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
|
created_date: datetime
|
||||||
|
last_modified_date: datetime
|
||||||
|
version: int
|
||||||
name: str
|
name: str
|
||||||
|
comic_id: str
|
||||||
|
|
||||||
|
def to_response(volume: Volume) -> VolumeResponse:
|
||||||
|
response: VolumeResponse = VolumeResponse(
|
||||||
|
id=volume.id,
|
||||||
|
created_date=volume.created_date,
|
||||||
|
last_modified_date=volume.last_modified_date,
|
||||||
|
version=volume.version,
|
||||||
|
name=volume.name,
|
||||||
|
comic_id=volume.comic_id
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|||||||
@@ -1,9 +1,26 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from src.db.models.comic import WorkType
|
||||||
|
|
||||||
class AddWorkType(BaseModel):
|
class AddWorkType(BaseModel):
|
||||||
worktype: str
|
worktype: str
|
||||||
|
|
||||||
class WorktypeResponse(BaseModel):
|
class WorktypeResponse(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
|
created_date: datetime
|
||||||
|
last_modified_date: datetime
|
||||||
|
version: int
|
||||||
name: str
|
name: str
|
||||||
|
|
||||||
|
|
||||||
|
def to_response(worktype: WorkType) -> WorktypeResponse:
|
||||||
|
response: WorktypeResponse = WorktypeResponse(
|
||||||
|
id=worktype.id,
|
||||||
|
created_date=worktype.created_date,
|
||||||
|
last_modified_date=worktype.last_modified_date,
|
||||||
|
version=worktype.version,
|
||||||
|
name=worktype.name
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from src.db.models.media import MediaArticle
|
||||||
|
|
||||||
|
class MediaArticleResponse(BaseModel):
|
||||||
|
id: str
|
||||||
|
created_date: datetime
|
||||||
|
last_modified_date: datetime
|
||||||
|
version: int
|
||||||
|
review: bool = False
|
||||||
|
title: Optional[str] = None
|
||||||
|
url: Optional[str] = None
|
||||||
|
|
||||||
|
class AddLink(BaseModel):
|
||||||
|
url: str
|
||||||
|
|
||||||
|
def to_response(video: MediaArticle) -> MediaArticleResponse:
|
||||||
|
response: MediaArticleResponse = MediaArticleResponse(
|
||||||
|
id=video.id,
|
||||||
|
created_date=video.created_date,
|
||||||
|
last_modified_date=video.last_modified_date,
|
||||||
|
version=video.version,
|
||||||
|
review=video.review,
|
||||||
|
title=video.title,
|
||||||
|
url=video.url,
|
||||||
|
)
|
||||||
|
return response
|
||||||
@@ -1,5 +1,38 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from src.db.models.media import MediaVideo
|
||||||
|
|
||||||
|
class MediaVideoResponse(BaseModel):
|
||||||
|
id: str
|
||||||
|
created_date: datetime
|
||||||
|
last_modified_date: datetime
|
||||||
|
version: int
|
||||||
|
cloud_link: Optional[str] = None
|
||||||
|
file_name: Optional[str] = None
|
||||||
|
path: Optional[str] = None
|
||||||
|
review: bool = False
|
||||||
|
title: Optional[str] = None
|
||||||
|
url: Optional[str] = None
|
||||||
|
should_download: bool = False
|
||||||
|
|
||||||
class AddLink(BaseModel):
|
class AddLink(BaseModel):
|
||||||
url: str
|
url: str
|
||||||
|
|
||||||
|
def to_response(video: MediaVideo) -> MediaVideoResponse:
|
||||||
|
response: MediaVideoResponse = MediaVideoResponse(
|
||||||
|
id=video.id,
|
||||||
|
created_date=video.created_date,
|
||||||
|
last_modified_date=video.last_modified_date,
|
||||||
|
version=video.version,
|
||||||
|
cloud_link=video.cloud_link,
|
||||||
|
file_name=video.file_name,
|
||||||
|
path=video.path,
|
||||||
|
review=video.review,
|
||||||
|
title=video.title,
|
||||||
|
url=video.url,
|
||||||
|
should_download=video.should_download
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from src.db.models.admin import Assignment
|
||||||
|
|
||||||
|
|
||||||
|
class AssignmentResponse(BaseModel):
|
||||||
|
id: str
|
||||||
|
created_date: datetime
|
||||||
|
last_modified_date: datetime
|
||||||
|
version: int
|
||||||
|
profile_id: str
|
||||||
|
permission_id: str
|
||||||
|
|
||||||
|
|
||||||
|
def to_response(assignment: Assignment) -> AssignmentResponse:
|
||||||
|
response: AssignmentResponse = AssignmentResponse(
|
||||||
|
id=assignment.id,
|
||||||
|
created_date=assignment.created_date,
|
||||||
|
last_modified_date=assignment.last_modified_date,
|
||||||
|
version=assignment.version,
|
||||||
|
profile_id=assignment.profile_id,
|
||||||
|
permission_id=assignment.permission_id
|
||||||
|
)
|
||||||
|
return response
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from src.db.models.admin import Permission
|
||||||
|
|
||||||
|
|
||||||
|
class PermissionResponse(BaseModel):
|
||||||
|
id: str
|
||||||
|
created_date: datetime
|
||||||
|
last_modified_date: datetime
|
||||||
|
version: int
|
||||||
|
name: str
|
||||||
|
|
||||||
|
|
||||||
|
def to_response(permission: Permission) -> PermissionResponse:
|
||||||
|
response: PermissionResponse = PermissionResponse(
|
||||||
|
id=permission.id,
|
||||||
|
created_date=permission.created_date,
|
||||||
|
last_modified_date=permission.last_modified_date,
|
||||||
|
version=permission.version,
|
||||||
|
name=permission.name
|
||||||
|
)
|
||||||
|
return response
|
||||||
@@ -1,12 +1,26 @@
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from src.db.models.admin import Profile
|
||||||
|
|
||||||
|
|
||||||
class ProfileResponse(BaseModel):
|
class ProfileResponse(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
user_name: str
|
username: str
|
||||||
|
|
||||||
|
|
||||||
class ProfileModel(BaseModel):
|
class ProfileModel(BaseModel):
|
||||||
user_name: str
|
username: str
|
||||||
|
email: str
|
||||||
first_name: str
|
first_name: str
|
||||||
last_name: str
|
last_name: str
|
||||||
|
active: bool
|
||||||
|
|
||||||
|
def to_model(profile: Profile) -> ProfileModel:
|
||||||
|
model: ProfileModel = ProfileModel(
|
||||||
|
username=profile.user_name,
|
||||||
|
email=profile.email,
|
||||||
|
first_name=profile.first_name,
|
||||||
|
last_name=profile.last_name,
|
||||||
|
active=profile.enabled,
|
||||||
|
)
|
||||||
|
return model
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from src.db.models.admin import Token
|
||||||
|
|
||||||
|
|
||||||
|
class TokenResponse(BaseModel):
|
||||||
|
id: str
|
||||||
|
created_date: datetime
|
||||||
|
last_modified_date: datetime
|
||||||
|
version: int
|
||||||
|
token: str
|
||||||
|
name: str
|
||||||
|
last_used_date: datetime
|
||||||
|
enabled: bool
|
||||||
|
profile_id: str
|
||||||
|
|
||||||
|
|
||||||
|
def to_response(token: Token) -> TokenResponse:
|
||||||
|
response: TokenResponse = TokenResponse(
|
||||||
|
id=token.id,
|
||||||
|
created_date=token.created_date,
|
||||||
|
last_modified_date=token.last_modified_date,
|
||||||
|
version=token.version,
|
||||||
|
token=token.token,
|
||||||
|
name=token.name,
|
||||||
|
last_used_date=token.last_used_date,
|
||||||
|
enabled=token.enabled,
|
||||||
|
profile_id=token.profile_id
|
||||||
|
)
|
||||||
|
return response
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
from src.apis.version1.admin.token import login_for_token_cookie
|
from src.apis.version1.admin.login import login_for_token_cookie
|
||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from fastapi import Request
|
from fastapi import Request
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ from src.webapps.media import route_actors, route_media, route_videos
|
|||||||
templates = Jinja2Templates(directory="src/templates")
|
templates = Jinja2Templates(directory="src/templates")
|
||||||
|
|
||||||
api_router = APIRouter()
|
api_router = APIRouter()
|
||||||
api_router.include_router(route_comics.router)
|
api_router.include_router(route_comics.router, tags=["webapp"])
|
||||||
api_router.include_router(route_artists.router)
|
api_router.include_router(route_artists.router, tags=["webapp"])
|
||||||
api_router.include_router(route_worktype.router)
|
api_router.include_router(route_worktype.router, tags=["webapp"])
|
||||||
api_router.include_router(route_media.router)
|
api_router.include_router(route_media.router, tags=["webapp"])
|
||||||
api_router.include_router(route_actors.router)
|
api_router.include_router(route_actors.router, tags=["webapp"])
|
||||||
api_router.include_router(route_videos.router)
|
api_router.include_router(route_videos.router, tags=["webapp"])
|
||||||
api_router.include_router(route_login.router)
|
api_router.include_router(route_login.router, tags=["webapp"])
|
||||||
api_router.include_router(route_admin.router)
|
api_router.include_router(route_admin.router, tags=["webapp"])
|
||||||
|
|
||||||
@api_router.get("/")
|
@api_router.get("/", tags=["webapp"])
|
||||||
def home(request: Request, msg: str | None = None):
|
def home(request: Request, msg: str | None = None):
|
||||||
return templates.TemplateResponse("index.html", {"request": request, "msg": msg})
|
return templates.TemplateResponse("index.html", {"request": request, "msg": msg})
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from src.schema.comics.comic import ComicSchema
|
|||||||
from src.webapps.comic.forms.comic import ValidateComicForm
|
from src.webapps.comic.forms.comic import ValidateComicForm
|
||||||
|
|
||||||
templates = Jinja2Templates(directory="src/templates")
|
templates = Jinja2Templates(directory="src/templates")
|
||||||
router = APIRouter(include_in_schema=False, prefix="/comic")
|
router = APIRouter(include_in_schema=True, prefix="/comic")
|
||||||
|
|
||||||
@router.get("/comics")
|
@router.get("/comics")
|
||||||
def get_comics(db: SessionDep, request: Request, msg: str | None = None):
|
def get_comics(db: SessionDep, request: Request, msg: str | None = None):
|
||||||
@@ -58,7 +58,7 @@ async def validate_comic(request: Request, db: SessionDep, comic_id: str, action
|
|||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
try:
|
try:
|
||||||
comic = ComicSchema(**form.__dict__)
|
comic = ComicSchema(**form.__dict__)
|
||||||
comic = update_comic(comic=comic, comic_id=comic_id, db=db)
|
comic = update_comic(new_comic=comic, comic_id=comic_id, db=db)
|
||||||
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)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import pytest
|
|||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
from sqlalchemy.orm import sessionmaker
|
from sqlalchemy.orm import Session, sessionmaker
|
||||||
|
|
||||||
from src.apis.base import api_router
|
from src.apis.base import api_router
|
||||||
from src.db.models.base import Base
|
from src.db.models.base import Base
|
||||||
@@ -45,7 +45,7 @@ def app() -> Generator[FastAPI, Any, None]:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def db_session(app: FastAPI) -> Generator[SessionTesting, Any, None]:
|
def db_session(app: FastAPI) -> Generator[Session, Any, None]:
|
||||||
connection = engine.connect()
|
connection = engine.connect()
|
||||||
transaction = connection.begin()
|
transaction = connection.begin()
|
||||||
session = SessionTesting(bind=connection)
|
session = SessionTesting(bind=connection)
|
||||||
@@ -57,7 +57,7 @@ def db_session(app: FastAPI) -> Generator[SessionTesting, Any, None]:
|
|||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def client(
|
def client(
|
||||||
app: FastAPI, db_session: SessionTesting
|
app: FastAPI, db_session: Session
|
||||||
) -> Generator[TestClient, Any, None]:
|
) -> Generator[TestClient, Any, None]:
|
||||||
"""
|
"""
|
||||||
Create a new FastAPI TestClient that uses the `db_session` fixture to override
|
Create a new FastAPI TestClient that uses the `db_session` fixture to override
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ MAPPING: Dict[str, str] = {
|
|||||||
"permission": "api/user/permissions",
|
"permission": "api/user/permissions",
|
||||||
"assignment": "api/user/assignments",
|
"assignment": "api/user/assignments",
|
||||||
"token": "api/user/tokens",
|
"token": "api/user/tokens",
|
||||||
"mail_account": "api/admin/mailaccount",
|
"mail_account": "api/admin/mailaccounts",
|
||||||
}
|
}
|
||||||
|
|
||||||
class EndPointNotAvailableException(Exception):
|
class EndPointNotAvailableException(Exception):
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ if __name__== "__main__":
|
|||||||
try:
|
try:
|
||||||
data = server.request(logger, table=table)
|
data = server.request(logger, table=table)
|
||||||
logger.info("%s: %s", table, len(data))
|
logger.info("%s: %s", table, len(data))
|
||||||
if len(data) == 1:
|
#if len(data) == 1:
|
||||||
logger.info("show data: %s", data)
|
# logger.info("show data: %s", data)
|
||||||
except EndPointNotAvailableException:
|
except EndPointNotAvailableException:
|
||||||
logger.info("Endpoint not implemented")
|
logger.info("Endpoint not implemented")
|
||||||
logger.info("kontor.sync finished")
|
logger.info("kontor.sync finished")
|
||||||
|
|||||||
Reference in New Issue
Block a user