add login functions for cookie and non-cookie authentication

This commit is contained in:
Thomas Peetz
2025-11-05 21:48:25 +01:00
parent 09c2a350e4
commit f3e47126b3
24 changed files with 279 additions and 142 deletions
-48
View File
@@ -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
+26 -42
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
@@ -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()
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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()
+142 -4
View File
@@ -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
+2 -2
View File
@@ -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
+16 -1
View File
@@ -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
+6 -2
View File
@@ -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)]
+14 -1
View File
@@ -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
+4
View File
@@ -8,3 +8,7 @@ class ArtistCreation(BaseModel):
class ArtistResponse(BaseModel):
id: str
name: str
class AddArtist(BaseModel):
id: str
name: str
@@ -23,8 +23,8 @@
<input type="text" required class="form-control" name="artist.link" value="{{artist_link}}" placeholder="Web link for artist here">
</div>
<div>
<button type="submit" class="btn btn-primary">Submit</button>
<button type="cancel" class="btn btn-primary">Cancel</button>
<button type="submit" class="btn btn-primary" name="action" value="submit">Submit</button>
<button type="cancel" class="btn btn-primary" name="action" value="cancel">Cancel</button>
</div>
</form>
</div>
+6 -8
View File
@@ -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})
+8 -7
View File
@@ -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="")
@@ -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=}"
+11 -5
View File
@@ -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)
+2 -2
View File
@@ -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()
@@ -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
+1 -4
View File
@@ -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")
+4 -5
View File
@@ -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})
+2 -2
View File
@@ -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