Files
api/src/main.py
T
2023-10-27 22:30:59 +02:00

163 lines
6.0 KiB
Python

from datetime import timedelta
from typing import Optional
from fastapi import FastAPI, APIRouter, Depends, Security
from fastapi_jwt import JwtAuthorizationCredentials, JwtAccessBearerCookie, JwtRefreshBearerCookie
from sqlalchemy import select
from starlette.background import BackgroundTasks
from starlette.middleware.cors import CORSMiddleware
from starlette.responses import RedirectResponse, Response
from starlette.staticfiles import StaticFiles
from strawberry.fastapi import GraphQLRouter
from config import APP_SECRET_KEY, GRAPHIQL, APP_DEBUG, ALLOW_CORS_ORIGINS
from database import models, async_session
from endpoints.login import LoginEndpoint, LoginInput, RefreshEndpoint, LogoutEndpoint
from endpoints.photo_editor_preview import PhotoEditorEndpoint
from endpoints.registration import RegistrationInput, RegistrationEndpoint
from graphql_schema.schema import schema, GraphQLContext
class App:
api_router = APIRouter(dependencies=[])
access_security = JwtAccessBearerCookie(
secret_key=APP_SECRET_KEY,
auto_error=False,
access_expires_delta=timedelta(minutes=20),
)
refresh_security = JwtRefreshBearerCookie(
secret_key=APP_SECRET_KEY,
auto_error=True,
refresh_expires_delta=timedelta(days=30),
)
def create_app(self):
app = FastAPI()
self.setup_exception_handlers(app)
self.setup_middleware(app)
self.setup_static_paths(app)
self.setup_routes(app)
return app
@staticmethod
def setup_exception_handlers(app: FastAPI):
pass
@staticmethod
def setup_middleware(app: FastAPI):
app.add_middleware(
CORSMiddleware,
allow_origins=ALLOW_CORS_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@staticmethod
def setup_static_paths(app: FastAPI):
app.mount("/uploads", StaticFiles(directory="/app/uploads"), name="uploads")
app.mount("/static", StaticFiles(directory="/app/static"), name="static")
def setup_graphql_endpoint(self, app: FastAPI):
async def setup_graphql_context(credentials: JwtAuthorizationCredentials = Security(self.access_security)):
user_id = credentials['id'] if credentials else None
organization_ids = set()
if user_id:
async with async_session() as db:
organization_ids = set((await db.scalars(
select(models.user_is_in_organization.c.organization_id)
.filter(models.user_is_in_organization.c.user_id == user_id)
)).all())
return GraphQLContext(
user_id=user_id,
organization_ids=organization_ids,
jwt_auth_credentials=credentials,
jwt=self.access_security,
background_tasks=Depends(BackgroundTasks)
)
graphql_app = GraphQLRouter(
schema,
graphiql=GRAPHIQL,
debug=APP_DEBUG,
context_getter=setup_graphql_context
)
app.include_router(graphql_app, prefix="/graphql")
def setup_routes(self, app: FastAPI):
self.setup_graphql_endpoint(app)
if APP_DEBUG:
@self.api_router.get("/graphql/autologin")
async def autologin():
access_token = self.access_security.create_access_token(subject={"id": 6, "name": "Franta Vomacka"})
response = RedirectResponse(url="/graphql")
self.access_security.set_access_cookie(response, access_token, expires_delta=timedelta(days=14))
return response
@self.api_router.post("/refresh")
async def refresh(
resp: Response,
credentials: JwtAuthorizationCredentials = Security(self.refresh_security)
):
return await RefreshEndpoint(
access_token=self.access_security,
refresh_token=self.refresh_security
).on_post(resp, credentials)
@self.api_router.post("/login")
async def login(resp: Response, user: LoginInput):
return await LoginEndpoint(
access_token=self.access_security,
refresh_token=self.refresh_security
).on_post(user, resp)
@self.api_router.post("/logout")
async def logout(resp: Response):
return await LogoutEndpoint(
access_token=self.access_security,
refresh_token=self.refresh_security
).on_post(resp)
@self.api_router.get("/photo/editor-preview/{photo_id}")
async def photo_editor_preview(
photo_id: int,
brightness: Optional[float] = None,
contrast: Optional[float] = None,
saturation: Optional[float] = None,
sharpness: Optional[float] = None,
rotate: Optional[float] = None,
crop_left: Optional[float] = None,
crop_top: Optional[float] = None,
crop_width: Optional[float] = None,
crop_height: Optional[float] = None,
):
return await PhotoEditorEndpoint(
access_token=self.access_security,
refresh_token=self.refresh_security
).show_preview(
photo_id=photo_id,
logged_user_id=0,
saturation=saturation,
brightness=brightness,
contrast=contrast,
sharpness=sharpness,
crop_top=crop_top,
crop_left=crop_left,
crop_height=crop_height,
crop_width=crop_width,
rotate=rotate,
)
@self.api_router.post("/registration", status_code=201)
async def registration(user: RegistrationInput):
return await RegistrationEndpoint().on_post(user)
# musi byt na konci
app.include_router(self.api_router)