mirror of
https://github.com/myronblair/epic-download
synced 2026-06-30 17:51:00 -05:00
auto-commit for 020628f5-4bfa-4157-a41e-90eec0ddfeec
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# Routes module
|
||||
@@ -0,0 +1,61 @@
|
||||
from fastapi import APIRouter, HTTPException, Depends
|
||||
from models.schemas import AdminLogin
|
||||
from auth import hash_password, verify_password, create_access_token
|
||||
from motor.motor_asyncio import AsyncIOMotorClient
|
||||
import os
|
||||
|
||||
router = APIRouter(prefix="/api/auth", tags=["Authentication"])
|
||||
|
||||
# MongoDB connection will be injected
|
||||
db = None
|
||||
|
||||
def set_db(database):
|
||||
global db
|
||||
db = database
|
||||
|
||||
@router.post("/login")
|
||||
async def login(credentials: AdminLogin):
|
||||
"""Admin login endpoint"""
|
||||
# Find admin user
|
||||
admin = await db.admin_users.find_one({"email": credentials.email})
|
||||
|
||||
if not admin:
|
||||
raise HTTPException(status_code=401, detail="Invalid email or password")
|
||||
|
||||
# Verify password
|
||||
if not verify_password(credentials.password, admin["password_hash"]):
|
||||
raise HTTPException(status_code=401, detail="Invalid email or password")
|
||||
|
||||
# Create access token
|
||||
access_token = create_access_token(data={"sub": admin["email"]})
|
||||
|
||||
return {
|
||||
"access_token": access_token,
|
||||
"token_type": "bearer",
|
||||
"email": admin["email"]
|
||||
}
|
||||
|
||||
@router.post("/verify")
|
||||
async def verify_token(admin: dict = Depends(lambda: __import__('auth').get_current_admin)):
|
||||
"""Verify JWT token"""
|
||||
return {"valid": True, "email": admin["email"]}
|
||||
|
||||
@router.post("/initialize-admin")
|
||||
async def initialize_admin():
|
||||
"""Initialize default admin user (for development/setup only)"""
|
||||
# Check if admin already exists
|
||||
existing_admin = await db.admin_users.find_one({"email": "admin@epictravel.com"})
|
||||
|
||||
if existing_admin:
|
||||
return {"message": "Admin user already exists"}
|
||||
|
||||
# Create default admin
|
||||
admin_data = {
|
||||
"email": "admin@epictravel.com",
|
||||
"password_hash": hash_password("admin123"),
|
||||
"created_at": __import__('datetime').datetime.utcnow()
|
||||
}
|
||||
|
||||
await db.admin_users.insert_one(admin_data)
|
||||
|
||||
return {"message": "Admin user created successfully", "email": "admin@epictravel.com"}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
from fastapi import APIRouter, HTTPException, Depends
|
||||
from typing import List, Optional
|
||||
from models.schemas import Destination, DestinationCreate, DestinationUpdate
|
||||
from auth import get_current_admin
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
router = APIRouter(prefix="/api/destinations", tags=["Destinations"])
|
||||
|
||||
# MongoDB connection will be injected
|
||||
db = None
|
||||
|
||||
def set_db(database):
|
||||
global db
|
||||
db = database
|
||||
|
||||
@router.get("", response_model=List[Destination])
|
||||
async def get_destinations(category: Optional[str] = None, search: Optional[str] = None):
|
||||
"""Get all destinations with optional filtering"""
|
||||
query = {}
|
||||
|
||||
if category and category != "All":
|
||||
query["category"] = category
|
||||
|
||||
if search:
|
||||
query["$or"] = [
|
||||
{"name": {"$regex": search, "$options": "i"}},
|
||||
{"location": {"$regex": search, "$options": "i"}}
|
||||
]
|
||||
|
||||
destinations = await db.destinations.find(query, {'_id': 0}).limit(100).to_list(100)
|
||||
|
||||
# Convert MongoDB _id to id for response
|
||||
for dest in destinations:
|
||||
if "_id" in dest:
|
||||
del dest["_id"]
|
||||
|
||||
return destinations
|
||||
|
||||
@router.get("/{destination_id}", response_model=Destination)
|
||||
async def get_destination(destination_id: str):
|
||||
"""Get a single destination by ID"""
|
||||
destination = await db.destinations.find_one({"id": destination_id})
|
||||
|
||||
if not destination:
|
||||
raise HTTPException(status_code=404, detail="Destination not found")
|
||||
|
||||
if "_id" in destination:
|
||||
del destination["_id"]
|
||||
|
||||
return destination
|
||||
|
||||
@router.post("", response_model=Destination)
|
||||
async def create_destination(
|
||||
destination: DestinationCreate,
|
||||
admin: dict = Depends(get_current_admin)
|
||||
):
|
||||
"""Create a new destination (admin only)"""
|
||||
destination_data = destination.dict()
|
||||
destination_data["id"] = str(uuid.uuid4())
|
||||
destination_data["created_at"] = datetime.utcnow()
|
||||
|
||||
await db.destinations.insert_one(destination_data)
|
||||
|
||||
if "_id" in destination_data:
|
||||
del destination_data["_id"]
|
||||
|
||||
return destination_data
|
||||
|
||||
@router.put("/{destination_id}", response_model=Destination)
|
||||
async def update_destination(
|
||||
destination_id: str,
|
||||
destination_update: DestinationUpdate,
|
||||
admin: dict = Depends(get_current_admin)
|
||||
):
|
||||
"""Update a destination (admin only)"""
|
||||
# Check if destination exists
|
||||
existing = await db.destinations.find_one({"id": destination_id})
|
||||
if not existing:
|
||||
raise HTTPException(status_code=404, detail="Destination not found")
|
||||
|
||||
# Update only provided fields
|
||||
update_data = {k: v for k, v in destination_update.dict().items() if v is not None}
|
||||
|
||||
if update_data:
|
||||
await db.destinations.update_one(
|
||||
{"id": destination_id},
|
||||
{"$set": update_data}
|
||||
)
|
||||
|
||||
# Fetch updated destination
|
||||
updated = await db.destinations.find_one({"id": destination_id})
|
||||
|
||||
if "_id" in updated:
|
||||
del updated["_id"]
|
||||
|
||||
return updated
|
||||
|
||||
@router.delete("/{destination_id}")
|
||||
async def delete_destination(
|
||||
destination_id: str,
|
||||
admin: dict = Depends(get_current_admin)
|
||||
):
|
||||
"""Delete a destination (admin only)"""
|
||||
result = await db.destinations.delete_one({"id": destination_id})
|
||||
|
||||
if result.deleted_count == 0:
|
||||
raise HTTPException(status_code=404, detail="Destination not found")
|
||||
|
||||
# Also delete any specials for this destination
|
||||
await db.specials.delete_many({"destination_id": destination_id})
|
||||
|
||||
return {"message": "Destination deleted successfully"}
|
||||
@@ -0,0 +1,82 @@
|
||||
from fastapi import APIRouter, HTTPException, File, UploadFile
|
||||
from fastapi.responses import FileResponse
|
||||
from typing import List
|
||||
from models.schemas import ContactCreate, NewsletterSubscribe
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
import uuid
|
||||
import os
|
||||
import shutil
|
||||
|
||||
router = APIRouter(prefix="/api", tags=["Other"])
|
||||
|
||||
# MongoDB connection will be injected
|
||||
db = None
|
||||
|
||||
# Upload directory setup
|
||||
UPLOAD_DIR = Path(__file__).parent.parent / 'uploads'
|
||||
UPLOAD_DIR.mkdir(exist_ok=True)
|
||||
|
||||
def set_db(database):
|
||||
global db
|
||||
db = database
|
||||
|
||||
@router.post("/contact")
|
||||
async def submit_contact(contact: ContactCreate):
|
||||
"""Submit a contact form"""
|
||||
contact_data = contact.dict()
|
||||
contact_data["id"] = str(uuid.uuid4())
|
||||
contact_data["created_at"] = datetime.utcnow()
|
||||
|
||||
await db.contacts.insert_one(contact_data)
|
||||
|
||||
return {"message": "Contact form submitted successfully"}
|
||||
|
||||
@router.post("/newsletter/subscribe")
|
||||
async def subscribe_newsletter(subscriber: NewsletterSubscribe):
|
||||
"""Subscribe to newsletter"""
|
||||
# Check if already subscribed
|
||||
existing = await db.newsletter_subscribers.find_one({"email": subscriber.email})
|
||||
if existing:
|
||||
return {"message": "Email already subscribed"}
|
||||
|
||||
subscriber_data = subscriber.dict()
|
||||
subscriber_data["id"] = str(uuid.uuid4())
|
||||
subscriber_data["subscribed_at"] = datetime.utcnow()
|
||||
|
||||
await db.newsletter_subscribers.insert_one(subscriber_data)
|
||||
|
||||
return {"message": "Successfully subscribed to newsletter"}
|
||||
|
||||
@router.post("/upload/image")
|
||||
async def upload_image(file: UploadFile = File(...)):
|
||||
"""Upload an image file"""
|
||||
# Validate file type
|
||||
allowed_extensions = [".jpg", ".jpeg", ".png", ".webp"]
|
||||
file_ext = os.path.splitext(file.filename)[1].lower()
|
||||
|
||||
if file_ext not in allowed_extensions:
|
||||
raise HTTPException(status_code=400, detail="Invalid file type. Allowed: jpg, jpeg, png, webp")
|
||||
|
||||
# Generate unique filename
|
||||
unique_filename = f"{uuid.uuid4()}{file_ext}"
|
||||
file_path = UPLOAD_DIR / unique_filename
|
||||
|
||||
# Save file
|
||||
with open(file_path, "wb") as buffer:
|
||||
shutil.copyfileobj(file.file, buffer)
|
||||
|
||||
# Return URL
|
||||
file_url = f"/api/uploads/{unique_filename}"
|
||||
|
||||
return {"url": file_url, "filename": unique_filename}
|
||||
|
||||
@router.get("/uploads/{filename}")
|
||||
async def get_uploaded_image(filename: str):
|
||||
"""Serve uploaded images"""
|
||||
file_path = UPLOAD_DIR / filename
|
||||
|
||||
if not file_path.exists():
|
||||
raise HTTPException(status_code=404, detail="Image not found")
|
||||
|
||||
return FileResponse(str(file_path))
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
from fastapi import APIRouter, HTTPException, Depends
|
||||
from typing import List
|
||||
from models.schemas import Special, SpecialCreate, SpecialUpdate
|
||||
from auth import get_current_admin
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
router = APIRouter(prefix="/api/specials", tags=["Specials"])
|
||||
|
||||
# MongoDB connection will be injected
|
||||
db = None
|
||||
|
||||
def set_db(database):
|
||||
global db
|
||||
db = database
|
||||
|
||||
@router.get("", response_model=List[Special])
|
||||
async def get_specials():
|
||||
"""Get all weekly specials"""
|
||||
specials = await db.specials.find({}, {'_id': 0}).limit(100).to_list(100)
|
||||
|
||||
# Convert MongoDB _id to id for response
|
||||
for special in specials:
|
||||
if "_id" in special:
|
||||
del special["_id"]
|
||||
|
||||
return specials
|
||||
|
||||
@router.get("/{special_id}", response_model=Special)
|
||||
async def get_special(special_id: str):
|
||||
"""Get a single special by ID"""
|
||||
special = await db.specials.find_one({"id": special_id})
|
||||
|
||||
if not special:
|
||||
raise HTTPException(status_code=404, detail="Special not found")
|
||||
|
||||
if "_id" in special:
|
||||
del special["_id"]
|
||||
|
||||
return special
|
||||
|
||||
@router.post("", response_model=Special)
|
||||
async def create_special(
|
||||
special: SpecialCreate,
|
||||
admin: dict = Depends(get_current_admin)
|
||||
):
|
||||
"""Add a destination to specials (admin only)"""
|
||||
# Check if destination exists
|
||||
destination = await db.destinations.find_one({"id": special.destination_id})
|
||||
if not destination:
|
||||
raise HTTPException(status_code=404, detail="Destination not found")
|
||||
|
||||
# Check if special already exists for this destination
|
||||
existing = await db.specials.find_one({"destination_id": special.destination_id})
|
||||
if existing:
|
||||
raise HTTPException(status_code=400, detail="Special already exists for this destination")
|
||||
|
||||
special_data = special.dict()
|
||||
special_data["id"] = str(uuid.uuid4())
|
||||
special_data["created_at"] = datetime.utcnow()
|
||||
|
||||
await db.specials.insert_one(special_data)
|
||||
|
||||
if "_id" in special_data:
|
||||
del special_data["_id"]
|
||||
|
||||
return special_data
|
||||
|
||||
@router.put("/{special_id}", response_model=Special)
|
||||
async def update_special(
|
||||
special_id: str,
|
||||
special_update: SpecialUpdate,
|
||||
admin: dict = Depends(get_current_admin)
|
||||
):
|
||||
"""Update a special (admin only)"""
|
||||
# Check if special exists
|
||||
existing = await db.specials.find_one({"id": special_id})
|
||||
if not existing:
|
||||
raise HTTPException(status_code=404, detail="Special not found")
|
||||
|
||||
# Update only provided fields
|
||||
update_data = {k: v for k, v in special_update.dict().items() if v is not None}
|
||||
|
||||
if update_data:
|
||||
await db.specials.update_one(
|
||||
{"id": special_id},
|
||||
{"$set": update_data}
|
||||
)
|
||||
|
||||
# Fetch updated special
|
||||
updated = await db.specials.find_one({"id": special_id})
|
||||
|
||||
if "_id" in updated:
|
||||
del updated["_id"]
|
||||
|
||||
return updated
|
||||
|
||||
@router.delete("/destination/{destination_id}")
|
||||
async def delete_special_by_destination(
|
||||
destination_id: str,
|
||||
admin: dict = Depends(get_current_admin)
|
||||
):
|
||||
"""Remove a destination from specials (admin only)"""
|
||||
result = await db.specials.delete_one({"destination_id": destination_id})
|
||||
|
||||
if result.deleted_count == 0:
|
||||
raise HTTPException(status_code=404, detail="Special not found for this destination")
|
||||
|
||||
return {"message": "Special removed successfully"}
|
||||
Reference in New Issue
Block a user