File size: 6,664 Bytes
5656488 5a25495 5656488 7946086 fc6834b 7946086 5656488 5a25495 5656488 7946086 6f78a73 5a25495 6f78a73 5a25495 5656488 1daf2f4 5656488 57401e6 1daf2f4 31213a9 7946086 6f78a73 7946086 4ac7918 7946086 5656488 ecdf76a 5656488 7946086 5656488 d02e3aa 289a1d6 d02e3aa db02b23 5656488 89d3a20 e8a9849 8aee62c 5a25495 06aff4f 1daf2f4 06aff4f 31213a9 4ac7918 31213a9 4ac7918 31213a9 4ac7918 31213a9 5a25495 57401e6 4ac7918 57401e6 4ac7918 5b63276 4ac7918 57401e6 4ac7918 57401e6 b580f2a b65cdff b580f2a b65cdff ee1083e b65cdff ee1083e 994da09 ee1083e 994da09 ee1083e 2fd4375 b580f2a ee1083e 9eddddd b580f2a 9eddddd b580f2a e34102f b580f2a 1aff32b 8055e78 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
import os
from typing import List, Optional
from fastapi import FastAPI, HTTPException, Request, Query, Depends
from fastapi.responses import JSONResponse
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from supabase import create_client, Client
from jose import jwt
SUPABASE_URL = os.getenv("SUPABASE_URL")
SUPABASE_KEY = os.getenv("SUPABASE_KEY")
JWT_KEY = os.getenv("JWT_KEY")
supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
class Recipe(BaseModel):
name: str
time: str
creator: str
category: str
diff: str
description: str
ingredients: List[str]
instructions: str
class RecipeUpdate(BaseModel):
name: Optional[str]
time: Optional[str]
creator: Optional[str]
category: Optional[str]
diff: Optional[str]
ingredients: Optional[List[str]]
description: Optional[str]
instructions: Optional[str]
class DeleteRecipeRequest(BaseModel):
id: str
security = HTTPBearer()
def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
token = credentials.credentials
try:
decoded = jwt.decode(
token,
JWT_KEY,
algorithms=["HS256"],
options={"verify_aud": False}
)
return decoded
except Exception as e:
raise HTTPException(status_code=401, detail="Invalid or expired authorization")
@app.put("/supabase/add/recipe")
def add_recipe_to_supabase(recipe: Recipe, token_data=Depends(verify_token)):
existing = supabase.table("recipes").select("name").eq("name", recipe.name).execute()
user_id = token_data['sub']
if existing.data:
raise HTTPException(status_code=400, detail="Recipe with this name already exists.")
try:
response = supabase.table("recipes").insert({
"name": recipe.name,
"time": recipe.time,
"creator": recipe.creator,
"user_id": user_id,
"category": recipe.category,
"diff": recipe.diff,
"description": recipe.description,
"ingredients": recipe.ingredients,
"instructions": recipe.instructions
}).execute()
except Exception as e:
raise HTTPException(status_code=500, detail=f"Insert failed: {str(e)}")
return {"message": "Recipe stored successfully!"}
@app.get("/supabase/recipes")
def get_all_recipes():
try:
response = supabase.table("recipes").select("*").execute()
data = response.data
return {
"rows": [{ "row": recipe } for recipe in data]
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to fetch recipes: {str(e)}")
@app.get("/supabase/myrecipes")
def get_my_recipes(user_id):
response = supabase.table("recipes").select("*").eq("user_id", user_id).execute()
data = response.data
return {
"rows": [{ "row": recipe } for recipe in data]
}
@app.get("/supabase/recipebyid")
async def get_recipe_by_id(id: str):
try:
response = supabase.table("recipes").select("*").eq("id", id).single().execute()
if response.data is None:
raise HTTPException(status_code=404, detail="Recipe not found")
return {"recipe": {"row": response.data}}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.delete("/supabase/delrecipe")
async def delete_recipe(data: DeleteRecipeRequest, token_data=Depends(verify_token)):
recipe = supabase.table("recipes").select("*").eq("id", data.id).single().execute()
user_id = token_data['sub']
if not recipe.data:
raise HTTPException(status_code=404, detail="Recipe not found")
if recipe.data["user_id"] != user_id:
raise HTTPException(status_code=403, detail="Deletion Aborted: Authorization required not found to complete this interaction")
supabase.table("recipes").delete().eq("id", data.id).execute()
return {"success": True, "message": "Recipe deleted"}
@app.get("/status")
def status():
return {"status": "ok"}
@app.patch("/supabase/edit/recipe")
async def edit_recipe(request: Request, id: str, update: RecipeUpdate, token_data=Depends(verify_token)):
user_id = token_data['sub']
update_dict = update.dict(exclude_none=True)
if not update_dict:
raise HTTPException(status_code=400, detail="No fields provided to update.")
ownership_check = supabase.table("recipes").select("user_id").eq("id", id).single().execute()
if ownership_check.data["user_id"] != user_id:
raise HTTPException(status_code=403, detail="Edit Aborted: Authorization required not found to complete this interaction")
response = supabase.table("recipes").update(update_dict).eq("id", id).execute()
return JSONResponse(content={
"message": "Recipe updated successfully.",
"data": response.data
})
@app.get("/supabase/recipes/paged")
def get_recipes_paged(
limit: int = Query(12, ge=1),
offset: int = Query(0, ge=0),
search: Optional[str] = None,
):
try:
if search:
query_name = supabase.table("recipes").select("*").filter("name", "ilike", f"%{search}%")
query_creator = supabase.table("recipes").select("*").filter("creator", "ilike", f"%{search}%")
response_name = query_name.execute()
response_creator = query_creator.execute()
raw_data = {recipe["id"]: recipe for recipe in response_name.data + response_creator.data}.values()
total_count = len(raw_data)
else:
query = supabase.table("recipes").select("*").range(offset, offset + limit - 1)
response = query.execute()
raw_data = response.data
total_count_response = supabase.table("recipes").select("id", count="exact").execute()
total_count = total_count_response.count if total_count_response else 0
paged_data = list(raw_data)[offset: offset + limit]
wrapped_data = [{"row": recipe} for recipe in paged_data]
return {
"rows": wrapped_data,
"offset": offset,
"limit": limit,
"total_count": total_count
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860) |