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)