Proxy the frontend site, use api prefix

This commit is contained in:
jableader 2024-09-21 10:34:13 +10:00
parent bc83e3c3d3
commit b1905cc03a

86
main.py
View file

@ -7,18 +7,9 @@ from typing import List, Annotated, Optional, Union
from fastapi import FastAPI, Depends, Query, Cookie from fastapi import FastAPI, Depends, Query, Cookie
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder from fastapi.encoders import jsonable_encoder
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI() app = FastAPI()
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*", "http://localhost:8080", "https://localhost:8080", "http://192.168.68.183:8080"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"]
)
# Dependency to create SQLite connection # Dependency to create SQLite connection
async def get_db(): async def get_db():
@ -31,14 +22,14 @@ async def get_db():
async def cookie_person(user_id: Annotated[int, Cookie(alias='user_id')], conn: sqlite3.Connection = Depends(get_db)) -> persons.Person: async def cookie_person(user_id: Annotated[int, Cookie(alias='user_id')], conn: sqlite3.Connection = Depends(get_db)) -> persons.Person:
return await persons.get_by_id(conn, user_id) return await persons.get_by_id(conn, user_id)
@app.get("/recipes/parse") @app.get("/api/recipes/parse")
async def parse_recipe_handler(url: str, conn: sqlite3.Connection = Depends(get_db), person = Depends(cookie_person)) -> recipes.Recipe: async def parse_recipe_handler(url: str, conn: sqlite3.Connection = Depends(get_db), person = Depends(cookie_person)) -> recipes.Recipe:
parsed = await recipes.parse_recipe(conn, person, url) parsed = await recipes.parse_recipe(conn, person, url)
if not parsed: if not parsed:
return JSONResponse(status_code=400, content={'message': 'Recipe not found'}) return JSONResponse(status_code=400, content={'message': 'Recipe not found'})
return parsed return parsed
@app.get("/recipes/ingredients/parse") @app.get("/api/recipes/ingredients/parse")
async def parse_ingredients(lines: Annotated[ async def parse_ingredients(lines: Annotated[
List[str], List[str],
Query(alias="ingredients", Query(alias="ingredients",
@ -69,7 +60,7 @@ class ProductUrl(BaseModel):
url: str url: str
tags: List[str] = [] tags: List[str] = []
@app.post("/products/") @app.post("/api/products")
async def create_product(url: ProductUrl, conn: sqlite3.Connection = Depends(get_db)) -> products.Product: async def create_product(url: ProductUrl, conn: sqlite3.Connection = Depends(get_db)) -> products.Product:
return await products.get_or_create(conn, url.url, url.tags) return await products.get_or_create(conn, url.url, url.tags)
@ -86,7 +77,7 @@ async def load_full_recipe(conn: sqlite3.Connection, id: int) -> recipes.Recipe:
return r return r
@app.get("/recipes/") @app.get("/api/recipes")
async def get_recipes(q: str | None = None, conn: sqlite3.Connection = Depends(get_db)) -> List[recipes.Recipe]: async def get_recipes(q: str | None = None, conn: sqlite3.Connection = Depends(get_db)) -> List[recipes.Recipe]:
result = [] result = []
if q: if q:
@ -103,7 +94,7 @@ async def get_recipes(q: str | None = None, conn: sqlite3.Connection = Depends(g
return result return result
@app.get("/recipes/{recipe_id}") @app.get("/api/recipes/{recipe_id}")
async def get_recipe(recipe_id: int, conn: sqlite3.Connection = Depends(get_db)) -> recipes.Recipe: async def get_recipe(recipe_id: int, conn: sqlite3.Connection = Depends(get_db)) -> recipes.Recipe:
r = await load_full_recipe(conn, recipe_id) r = await load_full_recipe(conn, recipe_id)
if not r: if not r:
@ -146,7 +137,7 @@ async def delete_recipe(recipe_id: int, conn: sqlite3.Connection = Depends(get_d
await conn.commit() await conn.commit()
return recipe return recipe
@app.get("/meals/upcoming") @app.get("/api/meals/upcoming")
async def get_upcoming_meals(date_from: Annotated[datetime.datetime, Query(alias='from')], to: datetime.datetime, conn: sqlite3.Connection = Depends(get_db)) -> List[meals.Meal]: async def get_upcoming_meals(date_from: Annotated[datetime.datetime, Query(alias='from')], to: datetime.datetime, conn: sqlite3.Connection = Depends(get_db)) -> List[meals.Meal]:
result = [] result = []
async for meal in meals.find_upcoming_meals_by_date_range(conn, date_from, to): async for meal in meals.find_upcoming_meals_by_date_range(conn, date_from, to):
@ -157,7 +148,7 @@ async def get_upcoming_meals(date_from: Annotated[datetime.datetime, Query(alias
return result return result
@app.get("/meals/{meal_id}") @app.get("/api/meals/{meal_id}")
async def get_meal(meal_id: int, conn: sqlite3.Connection = Depends(get_db)) -> meals.Meal: async def get_meal(meal_id: int, conn: sqlite3.Connection = Depends(get_db)) -> meals.Meal:
meal = await meals.find_meal_by_id(conn, meal_id) meal = await meals.find_meal_by_id(conn, meal_id)
if not meal: if not meal:
@ -201,7 +192,7 @@ def validate_meal(meal : meals.Meal) -> JSONResponse | None:
return None return None
@app.post("/meals/") @app.post("/api/meals")
async def create_meal(meal: meals.Meal, conn: sqlite3.Connection = Depends(get_db)) -> meals.Meal: async def create_meal(meal: meals.Meal, conn: sqlite3.Connection = Depends(get_db)) -> meals.Meal:
validation_response = validate_meal(meal) validation_response = validate_meal(meal)
if validation_response: if validation_response:
@ -211,7 +202,7 @@ async def create_meal(meal: meals.Meal, conn: sqlite3.Connection = Depends(get_d
await conn.commit() await conn.commit()
return meal return meal
@app.put("/meals/{meal_id}") @app.put("/api/meals/{meal_id}")
async def update_meal(meal_id: int, meal: meals.Meal, conn: sqlite3.Connection = Depends(get_db)) -> meals.Meal: async def update_meal(meal_id: int, meal: meals.Meal, conn: sqlite3.Connection = Depends(get_db)) -> meals.Meal:
if meal.id != meal_id: if meal.id != meal_id:
return JSONResponse(status_code=400, content={'message': 'Meal ID in URL does not match meal ID in body'}) return JSONResponse(status_code=400, content={'message': 'Meal ID in URL does not match meal ID in body'})
@ -229,7 +220,7 @@ async def update_meal(meal_id: int, meal: meals.Meal, conn: sqlite3.Connection =
return await get_meal(meal_id, conn) return await get_meal(meal_id, conn)
@app.post("/meals/{meal_id}/consumed") @app.post("/api/meals/{meal_id}/consumed")
async def mark_consumed(meal_id: int, consumed_date: Optional[datetime.datetime] = None, conn: sqlite3.Connection = Depends(get_db), person: persons.Person = Depends(cookie_person)) -> meals.Meal: async def mark_consumed(meal_id: int, consumed_date: Optional[datetime.datetime] = None, conn: sqlite3.Connection = Depends(get_db), person: persons.Person = Depends(cookie_person)) -> meals.Meal:
meal = await meals.find_meal_by_id(conn, meal_id) meal = await meals.find_meal_by_id(conn, meal_id)
if not meal: if not meal:
@ -239,7 +230,7 @@ async def mark_consumed(meal_id: int, consumed_date: Optional[datetime.datetime]
await conn.commit() await conn.commit()
return meal return meal
@app.delete("/meals/{meal_id}") @app.delete("/api/meals/{meal_id}")
async def delete_meal(meal_id: int, conn: sqlite3.Connection = Depends(get_db)) -> meals.Meal: async def delete_meal(meal_id: int, conn: sqlite3.Connection = Depends(get_db)) -> meals.Meal:
meal = await meals.find_meal_by_id(conn, meal_id) meal = await meals.find_meal_by_id(conn, meal_id)
if not meal: if not meal:
@ -249,7 +240,7 @@ async def delete_meal(meal_id: int, conn: sqlite3.Connection = Depends(get_db))
await conn.commit() await conn.commit()
return meal return meal
@app.get("/shopping/{list_id}") @app.get("/api/shopping/{list_id}")
async def get_shopping_list(list_id: Union[int, str], conn: sqlite3.Connection = Depends(get_db)) -> shopping.ShoppingList: async def get_shopping_list(list_id: Union[int, str], conn: sqlite3.Connection = Depends(get_db)) -> shopping.ShoppingList:
if list_id.lower() == 'current': if list_id.lower() == 'current':
return await shopping.current_shopping_list(conn) return await shopping.current_shopping_list(conn)
@ -261,7 +252,7 @@ async def get_shopping_list(list_id: Union[int, str], conn: sqlite3.Connection =
return await shopping.load_shopping_list(conn, list_id) return await shopping.load_shopping_list(conn, list_id)
@app.post("/shopping/current/purchased") @app.post("/api/shopping/current/purchased")
async def mark_purchased(conn: sqlite3.Connection = Depends(get_db), person: persons.Person = Depends(cookie_person)) -> shopping.ShoppingList: async def mark_purchased(conn: sqlite3.Connection = Depends(get_db), person: persons.Person = Depends(cookie_person)) -> shopping.ShoppingList:
response = await shopping.mark_purchased(conn) response = await shopping.mark_purchased(conn)
@ -274,7 +265,7 @@ class FoundResult(BaseModel):
created: List[shopping.ShoppingListResult] = [] created: List[shopping.ShoppingListResult] = []
removed: List[shopping.ShoppingListResult] = [] removed: List[shopping.ShoppingListResult] = []
@app.post("/shopping/current/found") @app.post("/api/shopping/current/found")
async def mark_shopping_list(ingredients: List[ingredients.Ingredient], conn: sqlite3.Connection = Depends(get_db)) -> FoundResult: async def mark_shopping_list(ingredients: List[ingredients.Ingredient], conn: sqlite3.Connection = Depends(get_db)) -> FoundResult:
now = datetime.datetime.now() now = datetime.datetime.now()
result = FoundResult() result = FoundResult()
@ -288,18 +279,18 @@ async def mark_shopping_list(ingredients: List[ingredients.Ingredient], conn: sq
await conn.commit() await conn.commit()
return result return result
@app.delete("/shopping/current/found/{product_id}") @app.delete("/api/shopping/current/found/{product_id}")
async def unmark_shopping_list(product_id: int, conn: sqlite3.Connection = Depends(get_db)) -> List[shopping.ShoppingListResult]: async def unmark_shopping_list(product_id: int, conn: sqlite3.Connection = Depends(get_db)) -> List[shopping.ShoppingListResult]:
response = await shopping.unmark_found(conn, product_id) response = await shopping.unmark_found(conn, product_id)
await conn.commit() await conn.commit()
return response return response
@app.get("/shopping/current/me/ingredients") @app.get("/api/shopping/current/me/ingredients")
async def get_my_shopping_list(conn: sqlite3.Connection = Depends(get_db), person: persons.Person = Depends(cookie_person)) -> List[shopping.ShoppingListRequest]: async def get_my_shopping_list(conn: sqlite3.Connection = Depends(get_db), person: persons.Person = Depends(cookie_person)) -> List[shopping.ShoppingListRequest]:
current = await shopping.current_shopping_list(conn) current = await shopping.current_shopping_list(conn)
return [r async for r in shopping.get_persons_requests(conn, current, person) if r.ingredient] return [r async for r in shopping.get_persons_requests(conn, current, person) if r.ingredient]
@app.post("/shopping/current/me/ingredients") @app.post("/api/shopping/current/me/ingredients")
async def sync_my_shopping_list(requests: List[ingredients.Ingredient], conn: sqlite3.Connection = Depends(get_db), person: persons.Person = Depends(cookie_person)) -> List[shopping.ShoppingListRequest]: async def sync_my_shopping_list(requests: List[ingredients.Ingredient], conn: sqlite3.Connection = Depends(get_db), person: persons.Person = Depends(cookie_person)) -> List[shopping.ShoppingListRequest]:
current = await shopping.current_shopping_list(conn) current = await shopping.current_shopping_list(conn)
result = [r async for r in shopping.sync_persons_requested_ingredients(conn, current, person, requests) if r.ingredient] result = [r async for r in shopping.sync_persons_requested_ingredients(conn, current, person, requests) if r.ingredient]
@ -309,7 +300,7 @@ async def sync_my_shopping_list(requests: List[ingredients.Ingredient], conn: sq
class MealIdWrapper(BaseModel): class MealIdWrapper(BaseModel):
meal_id: int meal_id: int
@app.post("/shopping/current/meals/me") @app.post("/api/shopping/current/meals/me")
async def request_meal(r: MealIdWrapper, conn: sqlite3.Connection = Depends(get_db), person: persons.Person = Depends(cookie_person)) -> shopping.ShoppingListRequest: async def request_meal(r: MealIdWrapper, conn: sqlite3.Connection = Depends(get_db), person: persons.Person = Depends(cookie_person)) -> shopping.ShoppingListRequest:
current = await shopping.current_shopping_list(conn) current = await shopping.current_shopping_list(conn)
meal = await meals.find_meal_by_id(conn, r.meal_id) meal = await meals.find_meal_by_id(conn, r.meal_id)
@ -320,7 +311,7 @@ async def request_meal(r: MealIdWrapper, conn: sqlite3.Connection = Depends(get_
await conn.commit() await conn.commit()
return response return response
@app.delete("/shopping/current/meals/{meal_id}") @app.delete("/api/shopping/current/meals/{meal_id}")
async def unrequest_meal(meal_id: int, conn: sqlite3.Connection = Depends(get_db), person: persons.Person = Depends(cookie_person)) -> dict: async def unrequest_meal(meal_id: int, conn: sqlite3.Connection = Depends(get_db), person: persons.Person = Depends(cookie_person)) -> dict:
current = await shopping.current_shopping_list(conn) current = await shopping.current_shopping_list(conn)
meal = await meals.find_meal_by_id(conn, meal_id) meal = await meals.find_meal_by_id(conn, meal_id)
@ -331,7 +322,7 @@ async def unrequest_meal(meal_id: int, conn: sqlite3.Connection = Depends(get_db
await conn.commit() await conn.commit()
return {} return {}
@app.get("/persons/") @app.get("/api/persons")
async def get_persons(q: str = None, conn: sqlite3.Connection = Depends(get_db)) -> List[meals.Person]: async def get_persons(q: str = None, conn: sqlite3.Connection = Depends(get_db)) -> List[meals.Person]:
query = persons.search_by_name(conn, q) if q else persons.get_all(conn) query = persons.search_by_name(conn, q) if q else persons.get_all(conn)
result = [] result = []
@ -340,7 +331,7 @@ async def get_persons(q: str = None, conn: sqlite3.Connection = Depends(get_db))
return result return result
@app.post("/persons/") @app.post("/api/persons")
async def create_person(person: persons.Person, conn: sqlite3.Connection = Depends(get_db)) -> persons.Person: async def create_person(person: persons.Person, conn: sqlite3.Connection = Depends(get_db)) -> persons.Person:
await persons.insert_person(conn, person) await persons.insert_person(conn, person)
await conn.commit() await conn.commit()
@ -349,7 +340,7 @@ async def create_person(person: persons.Person, conn: sqlite3.Connection = Depen
class LoginBody(BaseModel): class LoginBody(BaseModel):
username: str username: str
@app.post('/auth/login') @app.post('/api/auth/login')
async def login(data: LoginBody, conn: sqlite3.Connection = Depends(get_db)) -> persons.Person: async def login(data: LoginBody, conn: sqlite3.Connection = Depends(get_db)) -> persons.Person:
person = await persons.get_by_name(conn, data.username) person = await persons.get_by_name(conn, data.username)
if not person: if not person:
@ -359,6 +350,37 @@ async def login(data: LoginBody, conn: sqlite3.Connection = Depends(get_db)) ->
response.set_cookie(key='user_id', value=str(person.id)) response.set_cookie(key='user_id', value=str(person.id))
return response return response
@app.post('/auth/refresh') @app.post('/api/auth/refresh')
async def current_user(user: persons.Person = Depends(cookie_person)) -> persons.Person: async def current_user(user: persons.Person = Depends(cookie_person)) -> persons.Person:
return user return user
import os
if os.environ.get('DOOF_PROD', False):
from fastapi.staticfiles import StaticFiles
app.mount("/", StaticFiles(directory="./front-dist", html=True), name="front-dist")
else:
# Proxy the request to the frontend development server
from starlette.requests import Request
from starlette.responses import StreamingResponse
from starlette.background import BackgroundTask
import httpx
client = httpx.AsyncClient(base_url="http://localhost:8080/")
async def _reverse_proxy(request: Request):
url = httpx.URL(path=request.url.path,
query=request.url.query.encode("utf-8"))
rp_req = client.build_request(request.method, url,
headers=request.headers.raw,
content=request.stream())
rp_resp = await client.send(rp_req, stream=True)
return StreamingResponse(
rp_resp.aiter_raw(),
status_code=rp_resp.status_code,
headers=rp_resp.headers,
background=BackgroundTask(rp_resp.aclose),
)
app.add_route("/{path:path}",_reverse_proxy, ["GET", "POST"])