Tuesday, December 30, 2025

Now let me create all the files for the restaurant application: Action: file_editor create /app/backend/server.py --file-text "from fastapi import FastAPI, APIRouter, HTTPException, Request from dotenv import load_dotenv from starlette.middleware.cors import CORSMiddleware from motor.motor_asyncio import AsyncIOMotorClient import os import logging from pathlib import Path from pydantic import BaseModel, Field, ConfigDict from typing import List, Optional, Dict import uuid from datetime import datetime, timezone, date from emergentintegrations.payments.stripe.checkout import StripeCheckout, CheckoutSessionResponse, CheckoutStatusResponse, CheckoutSessionRequest ROOT_DIR = Path(__file__).parent load_dotenv(ROOT_DIR / '.env') # MongoDB connection mongo_url = os.environ['MONGO_URL'] client = AsyncIOMotorClient(mongo_url) db = client[os.environ['DB_NAME']] # Stripe setup stripe_api_key = os.environ.get('STRIPE_API_KEY') # Create the main app app = FastAPI() api_router = APIRouter(prefix=\"/api\") # ============ MODELS ============ class MenuItem(BaseModel): model_config = ConfigDict(extra=\"ignore\") id: str = Field(default_factory=lambda: str(uuid.uuid4())) name: str description: str price: float category: str # appetizers, mains, desserts, drinks image_url: Optional[str] = None is_available: bool = True is_special: bool = False special_price: Optional[float] = None class MenuItemCreate(BaseModel): name: str description: str price: float category: str image_url: Optional[str] = None is_available: bool = True is_special: bool = False special_price: Optional[float] = None class Reservation(BaseModel): model_config = ConfigDict(extra=\"ignore\") id: str = Field(default_factory=lambda: str(uuid.uuid4())) customer_name: str customer_email: str customer_phone: str date: str time_slot: str party_size: int special_requests: Optional[str] = None status: str = \"confirmed\" created_at: str = Field(default_factory=lambda: datetime.now(timezone.utc).isoformat()) class ReservationCreate(BaseModel): customer_name: str customer_email: str customer_phone: str date: str time_slot: str party_size: int special_requests: Optional[str] = None class CartItem(BaseModel): menu_item_id: str name: str price: float quantity: int class Order(BaseModel): model_config = ConfigDict(extra=\"ignore\") id: str = Field(default_factory=lambda: str(uuid.uuid4())) items: List[CartItem] total: float customer_name: str customer_email: str customer_phone: str order_type: str # pickup or delivery delivery_address: Optional[str] = None special_instructions: Optional[str] = None status: str = \"pending\" payment_status: str = \"pending\" session_id: Optional[str] = None created_at: str = Field(default_factory=lambda: datetime.now(timezone.utc).isoformat()) class OrderCreate(BaseModel): items: List[CartItem] customer_name: str customer_email: str customer_phone: str order_type: str delivery_address: Optional[str] = None special_instructions: Optional[str] = None class PaymentTransaction(BaseModel): model_config = ConfigDict(extra=\"ignore\") id: str = Field(default_factory=lambda: str(uuid.uuid4())) session_id: str order_id: str amount: float currency: str = \"usd\" payment_status: str = \"initiated\" metadata: Optional[Dict[str, str]] = None created_at: str = Field(default_factory=lambda: datetime.now(timezone.utc).isoformat()) updated_at: str = Field(default_factory=lambda: datetime.now(timezone.utc).isoformat()) class Special(BaseModel): model_config = ConfigDict(extra=\"ignore\") id: str = Field(default_factory=lambda: str(uuid.uuid4())) title: str description: str menu_item_id: Optional[str] = None discount_percent: Optional[int] = None valid_from: str valid_until: str image_url: Optional[str] = None is_active: bool = True class SpecialCreate(BaseModel): title: str description: str menu_item_id: Optional[str] = None discount_percent: Optional[int] = None valid_from: str valid_until: str image_url: Optional[str] = None class CheckoutRequest(BaseModel): order_id: str origin_url: str # ============ MENU ENDPOINTS ============ @api_router.get(\"/\") async def root(): return {\"message\": \"Welcome to The Golden Spoon API\"} @api_router.get(\"/menu\", response_model=List[MenuItem]) async def get_menu(): items = await db.menu_items.find({}, {\"_id\": 0}).to_list(1000) return items @api_router.get(\"/menu/category/{category}\", response_model=List[MenuItem]) async def get_menu_by_category(category: str): items = await db.menu_items.find({\"category\": category}, {\"_id\": 0}).to_list(1000) return items @api_router.get(\"/menu/{item_id}\", response_model=MenuItem) async def get_menu_item(item_id: str): item = await db.menu_items.find_one({\"id\": item_id}, {\"_id\": 0}) if not item: raise HTTPException(status_code=404, detail=\"Menu item not found\") return item @api_router.post(\"/menu\", response_model=MenuItem) async def create_menu_item(item: MenuItemCreate): menu_item = MenuItem(**item.model_dump()) doc = menu_item.model_dump() await db.menu_items.insert_one(doc) return menu_item @api_router.put(\"/menu/{item_id}\", response_model=MenuItem) async def update_menu_item(item_id: str, item: MenuItemCreate): existing = await db.menu_items.find_one({\"id\": item_id}) if not existing: raise HTTPException(status_code=404, detail=\"Menu item not found\") update_data = item.model_dump() await db.menu_items.update_one({\"id\": item_id}, {\"$set\": update_data}) updated = await db.menu_items.find_one({\"id\": item_id}, {\"_id\": 0}) return updated @api_router.delete(\"/menu/{item_id}\") async def delete_menu_item(item_id: str): result = await db.menu_items.delete_one({\"id\": item_id}) if result.deleted_count == 0: raise HTTPException(status_code=404, detail=\"Menu item not found\") return {\"message\": \"Item deleted\"} # ============ SPECIALS ENDPOINTS ============ @api_router.get(\"/specials\", response_model=List[Special]) async def get_specials(): specials = await db.specials.find({\"is_active\": True}, {\"_id\": 0}).to_list(100) return specials @api_router.post(\"/specials\", response_model=Special) async def create_special(special: SpecialCreate): special_obj = Special(**special.model_dump()) doc = special_obj.model_dump() await db.specials.insert_one(doc) return special_obj @api_router.delete(\"/specials/{special_id}\") async def delete_special(special_id: str): result = await db.specials.delete_one({\"id\": special_id}) if result.deleted_count == 0: raise HTTPException(status_code=404, detail=\"Special not found\") return {\"message\": \"Special deleted\"} # ============ RESERVATION ENDPOINTS ============ @api_router.get(\"/reservations/slots/{date_str}\") async def get_available_slots(date_str: str): # All possible time slots all_slots = [ \"11:00 AM\", \"11:30 AM\", \"12:00 PM\", \"12:30 PM\", \"1:00 PM\", \"1:30 PM\", \"2:00 PM\", \"2:30 PM\", \"5:00 PM\", \"5:30 PM\", \"6:00 PM\", \"6:30 PM\", \"7:00 PM\", \"7:30 PM\", \"8:00 PM\", \"8:30 PM\", \"9:00 PM\" ] # Get booked slots for this date booked = await db.reservations.find( {\"date\": date_str, \"status\": {\"$ne\": \"cancelled\"}}, {\"_id\": 0, \"time_slot\": 1} ).to_list(100) booked_slots = [r[\"time_slot\"] for r in booked] # Count bookings per slot (max 5 per slot) slot_counts = {} for slot in booked_slots: slot_counts[slot] = slot_counts.get(slot, 0) + 1 available = [s for s in all_slots if slot_counts.get(s, 0) < 5] return {\"date\": date_str, \"available_slots\": available} @api_router.get(\"/reservations\", response_model=List[Reservation]) async def get_reservations(): reservations = await db.reservations.find({}, {\"_id\": 0}).to_list(1000) return reservations @api_router.post(\"/reservations\", response_model=Reservation) async def create_reservation(reservation: ReservationCreate): # Check if slot is available slots_info = await get_available_slots(reservation.date) if reservation.time_slot not in slots_info[\"available_slots\"]: raise HTTPException(status_code=400, detail=\"This time slot is no longer available\") reservation_obj = Reservation(**reservation.model_dump()) doc = reservation_obj.model_dump() await db.reservations.insert_one(doc) return reservation_obj @api_router.put(\"/reservations/{reservation_id}/cancel\") async def cancel_reservation(reservation_id: str): result = await db.reservations.update_one( {\"id\": reservation_id}, {\"$set\": {\"status\": \"cancelled\"}} ) if result.matched_count == 0: raise HTTPException(status_code=404, detail=\"Reservation not found\") return {\"message\": \"Reservation cancelled\"} # ============ ORDER ENDPOINTS ============ @api_router.post(\"/orders\", response_model=Order) async def create_order(order: OrderCreate): order_obj = Order(**order.model_dump()) doc = order_obj.model_dump() await db.orders.insert_one(doc) return order_obj @api_router.get(\"/orders/{order_id}\", response_model=Order) async def get_order(order_id: str): order = await db.orders.find_one({\"id\": order_id}, {\"_id\": 0}) if not order: raise HTTPException(status_code=404, detail=\"Order not found\") return order # ============ PAYMENT ENDPOINTS ============ @api_router.post(\"/checkout\") async def create_checkout(checkout_req: CheckoutRequest, request: Request): # Get the order order = await db.orders.find_one({\"id\": checkout_req.order_id}, {\"_id\": 0}) if not order: raise HTTPException(status_code=404, detail=\"Order not found\") # Build URLs from origin origin = checkout_req.origin_url success_url = f\"{origin}/order-success?session_id={{CHECKOUT_SESSION_ID}}\" cancel_url = f\"{origin}/order\" # Initialize Stripe host_url = str(request.base_url) webhook_url = f\"{host_url}api/webhook/stripe\" stripe_checkout = StripeCheckout(api_key=stripe_api_key, webhook_url=webhook_url) # Create checkout session checkout_request = CheckoutSessionRequest( amount=float(order[\"total\"]), currency=\"usd\", success_url=success_url, cancel_url=cancel_url, metadata={ \"order_id\": checkout_req.order_id, \"customer_email\": order[\"customer_email\"] } ) session: CheckoutSessionResponse = await stripe_checkout.create_checkout_session(checkout_request) # Create payment transaction record payment = PaymentTransaction( session_id=session.session_id, order_id=checkout_req.order_id, amount=float(order[\"total\"]), currency=\"usd\", payment_status=\"initiated\", metadata={\"customer_email\": order[\"customer_email\"]} ) await db.payment_transactions.insert_one(payment.model_dump()) # Update order with session_id await db.orders.update_one( {\"id\": checkout_req.order_id}, {\"$set\": {\"session_id\": session.session_id}} ) return {\"url\": session.url, \"session_id\": session.session_id} @api_router.get(\"/checkout/status/{session_id}\") async def get_checkout_status(session_id: str, request: Request): host_url = str(request.base_url) webhook_url = f\"{host_url}api/webhook/stripe\" stripe_checkout = StripeCheckout(api_key=stripe_api_key, webhook_url=webhook_url) status: CheckoutStatusResponse = await stripe_checkout.get_checkout_status(session_id) # Update payment transaction payment = await db.payment_transactions.find_one({\"session_id\": session_id}, {\"_id\": 0}) if payment and payment[\"payment_status\"] != status.payment_status: await db.payment_transactions.update_one( {\"session_id\": session_id}, {\"$set\": { \"payment_status\": status.payment_status, \"updated_at\": datetime.now(timezone.utc).isoformat() }} ) # Update order status if paid if status.payment_status == \"paid\": await db.orders.update_one( {\"session_id\": session_id}, {\"$set\": {\"payment_status\": \"paid\", \"status\": \"confirmed\"}} ) return { \"status\": status.status, \"payment_status\": status.payment_status, \"amount_total\": status.amount_total, \"currency\": status.currency } @api_router.post(\"/webhook/stripe\") async def stripe_webhook(request: Request): body = await request.body() signature = request.headers.get(\"Stripe-Signature\") host_url = str(request.base_url) webhook_url = f\"{host_url}api/webhook/stripe\" stripe_checkout = StripeCheckout(api_key=stripe_api_key, webhook_url=webhook_url) try: webhook_response = await stripe_checkout.handle_webhook(body, signature) if webhook_response.payment_status == \"paid\": await db.payment_transactions.update_one( {\"session_id\": webhook_response.session_id}, {\"$set\": { \"payment_status\": \"paid\", \"updated_at\": datetime.now(timezone.utc).isoformat() }} ) await db.orders.update_one( {\"session_id\": webhook_response.session_id}, {\"$set\": {\"payment_status\": \"paid\", \"status\": \"confirmed\"}} ) return {\"status\": \"success\"} except Exception as e: logging.error(f\"Webhook error: {e}\") return {\"status\": \"error\", \"message\": str(e)} # ============ SEED DATA ENDPOINT ============ @api_router.post(\"/seed\") async def seed_data(): # Clear existing data await db.menu_items.delete_many({}) await db.specials.delete_many({}) # Seed menu items menu_items = [ # Appetizers {\"name\": \"Bruschetta Classica\", \"description\": \"Grilled bread rubbed with garlic, topped with fresh tomatoes, basil, and extra virgin olive oil\", \"price\": 12.00, \"category\": \"appetizers\", \"image_url\": \"https://images.pexels.com/photos/1095550/pexels-photo-1095550.jpeg\"}, {\"name\": \"Carpaccio di Manzo\", \"description\": \"Thinly sliced raw beef with arugula, capers, and shaved Parmigiano-Reggiano\", \"price\": 18.00, \"category\": \"appetizers\", \"image_url\": \"https://images.pexels.com/photos/8753672/pexels-photo-8753672.jpeg\"}, {\"name\": \"Arancini\", \"description\": \"Crispy fried risotto balls filled with mozzarella, served with marinara sauce\", \"price\": 14.00, \"category\": \"appetizers\", \"image_url\": \"https://images.pexels.com/photos/6287537/pexels-photo-6287537.jpeg\"}, {\"name\": \"Calamari Fritti\", \"description\": \"Lightly breaded and fried calamari with lemon aioli\", \"price\": 16.00, \"category\": \"appetizers\", \"image_url\": \"https://images.pexels.com/photos/4553111/pexels-photo-4553111.jpeg\"}, # Mains {\"name\": \"Spaghetti alla Carbonara\", \"description\": \"Classic Roman pasta with guanciale, egg yolk, Pecorino Romano, and black pepper\", \"price\": 24.00, \"category\": \"mains\", \"image_url\": \"https://images.pexels.com/photos/4518843/pexels-photo-4518843.jpeg\"}, {\"name\": \"Osso Buco\", \"description\": \"Braised veal shanks with gremolata, served with saffron risotto\", \"price\": 42.00, \"category\": \"mains\", \"image_url\": \"https://images.pexels.com/photos/6941010/pexels-photo-6941010.jpeg\"}, {\"name\": \"Risotto ai Funghi Porcini\", \"description\": \"Creamy Arborio rice with wild porcini mushrooms and truffle oil\", \"price\": 28.00, \"category\": \"mains\", \"image_url\": \"https://images.pexels.com/photos/3590401/pexels-photo-3590401.jpeg\"}, {\"name\": \"Pollo alla Parmigiana\", \"description\": \"Breaded chicken cutlet with marinara and melted mozzarella, served with pasta\", \"price\": 26.00, \"category\": \"mains\", \"image_url\": \"https://images.pexels.com/photos/5175621/pexels-photo-5175621.jpeg\"}, {\"name\": \"Branzino al Forno\", \"description\": \"Whole Mediterranean sea bass, oven-roasted with herbs and lemon\", \"price\": 38.00, \"category\": \"mains\", \"image_url\": \"https://images.pexels.com/photos/8963379/pexels-photo-8963379.jpeg\"}, {\"name\": \"Tagliata di Manzo\", \"description\": \"Sliced grilled ribeye with arugula, cherry tomatoes, and aged balsamic\", \"price\": 44.00, \"category\": \"mains\", \"image_url\": \"https://images.pexels.com/photos/3535383/pexels-photo-3535383.jpeg\"}, # Desserts {\"name\": \"Tiramisu\", \"description\": \"Classic Italian dessert with espresso-soaked ladyfingers and mascarpone cream\", \"price\": 12.00, \"category\": \"desserts\", \"image_url\": \"https://images.pexels.com/photos/6880219/pexels-photo-6880219.jpeg\"}, {\"name\": \"Panna Cotta\", \"description\": \"Silky vanilla cream with seasonal berry compote\", \"price\": 10.00, \"category\": \"desserts\", \"image_url\": \"https://images.pexels.com/photos/8697540/pexels-photo-8697540.jpeg\"}, {\"name\": \"Cannoli Siciliani\", \"description\": \"Crispy pastry shells filled with sweet ricotta and chocolate chips\", \"price\": 11.00, \"category\": \"desserts\", \"image_url\": \"https://images.pexels.com/photos/14471923/pexels-photo-14471923.jpeg\"}, # Drinks {\"name\": \"Chianti Classico\", \"description\": \"Glass of premium Tuscan red wine\", \"price\": 14.00, \"category\": \"drinks\", \"image_url\": \"https://images.pexels.com/photos/342971/pexels-photo-342971.jpeg\"}, {\"name\": \"Limoncello Spritz\", \"description\": \"Refreshing cocktail with limoncello, prosecco, and soda\", \"price\": 13.00, \"category\": \"drinks\", \"image_url\": \"https://images.pexels.com/photos/338713/pexels-photo-338713.jpeg\"}, {\"name\": \"Espresso\", \"description\": \"Traditional Italian espresso\", \"price\": 4.00, \"category\": \"drinks\", \"image_url\": \"https://images.pexels.com/photos/302899/pexels-photo-302899.jpeg\"}, {\"name\": \"San Pellegrino\", \"description\": \"Sparkling mineral water (750ml)\", \"price\": 6.00, \"category\": \"drinks\", \"image_url\": \"https://images.pexels.com/photos/1337825/pexels-photo-1337825.jpeg\"}, ] for item in menu_items: menu_item = MenuItem(**item) await db.menu_items.insert_one(menu_item.model_dump()) # Seed specials specials = [ { \"title\": \"Sunday Family Feast\", \"description\": \"Enjoy our special family-style dinner with antipasti, pasta, and dessert for the whole table\", \"discount_percent\": 20, \"valid_from\": \"2024-01-01\", \"valid_until\": \"2025-12-31\", \"image_url\": \"https://images.pexels.com/photos/1267320/pexels-photo-1267320.jpeg\" }, { \"title\": \"Happy Hour\", \"description\": \"Half-price appetizers and $10 house wines, Monday-Friday 4-6 PM\", \"discount_percent\": 50, \"valid_from\": \"2024-01-01\", \"valid_until\": \"2025-12-31\", \"image_url\": \"https://images.pexels.com/photos/696218/pexels-photo-696218.jpeg\" }, { \"title\": \"Chef's Tasting Menu\", \"description\": \"5-course culinary journey featuring seasonal ingredients - $85 per person\", \"valid_from\": \"2024-01-01\", \"valid_until\": \"2025-12-31\", \"image_url\": \"https://images.pexels.com/photos/9888285/pexels-photo-9888285.jpeg\" } ] for special in specials: special_obj = Special(**special) await db.specials.insert_one(special_obj.model_dump()) return {\"message\": \"Data seeded successfully\", \"menu_items\": len(menu_items), \"specials\": len(specials)} # Include router app.include_router(api_router) app.add_middleware( CORSMiddleware, allow_credentials=True, allow_origins=os.environ.get('CORS_ORIGINS', '*').split(','), allow_methods=[\"*\"], allow_headers=[\"*\"], ) logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) @app.on_event(\"shutdown\") async def shutdown_db_client(): client.close() " Observation: Overwrite successful: /app/backend/server.py

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.

የቦ ታክስ

ለዲያስፓራ አባላት በሙሉ እንዲሁም አሁን ኢትዮጵያ ላላችሁ። የአሜሪካ ታክሳችሁን ካላችሁበት ሆናችሁ እንድታሰሩ ነገሮችን ሁሉ አናስተካክላለናል። ያልተሰራ የታክስ ውዝፍ (Back Tax)፣ መስተካከል ያለበት ታክስ (Amendment) እንችላለን። የዚህ አመት ታክስ እና ሌሎችንም እንሰራለን።በViberም ሆነ Whatspp ይደውሉልን። ስልክ ቁጥራችን 619 255 5530 ነው ። YebboTax info@yebbo.com Yebbo.com

Translate

Do you need Ethiopian Power of Attorney where your agent can preform several crucial tasks on your behalf? Such as adoption proceedings, buying movable or immovable properties, paying tax, represent you in governmental and public offices and several others tasks with our your physical presence? If your answer is yes get the Ethiopian Power of Attorney or YEBBO now on sale

Shop Amazon

Call Yebbo

የቦ ኮሚኒኬሽን ኔት ወርክ ፣ ለ25 አመታት በላይ የስራ ልምድ ያካበተው የእናንተው በእናንተው። ከምሰጣቸው አገልግሎቶች ውስጥ የውክልና አገልግሎት መስጠት የኢትዮጵያ ፓስፖርት አገልግሎት መስጠት የቢጫ ካርድ የማውጣት አገልግሎት የታክስ አገልግሎት መስጠት (የትም የኢትዮጵያ ግዛት ይኑሩ) የጉዞ ወኪል የትርጉም ስራ አገልግሎት ለበለጠ መረጃ በስልክ ቁጥር 619-255-5530 ይደውሉ።