Upload files to "/"

This commit is contained in:
2026-05-25 14:07:00 +00:00
commit b2fe1b8502
17 changed files with 5418 additions and 0 deletions
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+11
View File
@@ -0,0 +1,11 @@
import cloudinary
import os
from dotenv import load_dotenv
load_dotenv()
cloudinary.config(
cloud_name=os.getenv("CLOUDINARY_CLOUD_NAME"),
api_key=os.getenv("CLOUDINARY_API_KEY"),
api_secret=os.getenv("CLOUDINARY_API_SECRET")
)
+1127
View File
File diff suppressed because it is too large Load Diff
+82
View File
@@ -0,0 +1,82 @@
#datatbase.py
import os
import psycopg2
from psycopg2 import OperationalError
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy.engine.url import URL
from dotenv import load_dotenv
# Load env variables
load_dotenv()
# Neon DB credentials
DB_NAME = os.getenv("DB_NAME")
DB_USER = os.getenv("DB_USER")
DB_PASSWORD = os.getenv("DB_PASSWORD")
DB_HOST = os.getenv("DB_HOST")
DB_PORT = os.getenv("DB_PORT", "5432")
# Create DATABASE URL
DATABASE_URL = URL.create(
drivername="postgresql",
username=DB_USER,
password=DB_PASSWORD,
host=DB_HOST,
port=DB_PORT,
database=DB_NAME,
query={
"sslmode": "require" # 🔥 MUST for Neon
}
)
# Create engine
engine = create_engine(
DATABASE_URL,
pool_pre_ping=True,
pool_recycle=300,
pool_size=5,
max_overflow=10,
echo=False
)
# Session
SessionLocal = sessionmaker(
autoflush=False,
autocommit=False,
bind=engine
)
# Base model
Base = declarative_base()
# Dependency for FastAPI
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# Test connection
def test_connection():
try:
connection = psycopg2.connect(
host=DB_HOST,
database=DB_NAME,
user=DB_USER,
password=DB_PASSWORD,
port=DB_PORT,
sslmode="require"
)
print("✅ Neon PostgreSQL connection successful!")
connection.close()
except OperationalError as e:
print(f"❌ Error: {e}")
print("Failed to connect to Neon database.")
if __name__ == "__main__":
test_connection()
+2846
View File
File diff suppressed because it is too large Load Diff
+405
View File
@@ -0,0 +1,405 @@
from sqlalchemy import Column, Integer, String, Float, Boolean, ForeignKey, DateTime, Text
from sqlalchemy.orm import relationship
from datetime import datetime
from sqlalchemy import Index
from database import Base
# =========================
# USER
# =========================
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, nullable=True)
phone = Column(String, unique=True, nullable=False)
email = Column(String, unique=True)
# 🔥 ADD THESE TWO NEW COLUMNS
dob = Column(String, nullable=True)
gender = Column(String, nullable=True)
created_at = Column(DateTime, default=datetime.utcnow)
frontend_ip = Column(String, nullable=True)
backend_ip = Column(String, nullable=True)
orders = relationship("Order", back_populates="user")
# 🔥 NEW RELATION
addresses = relationship("UserAddress", back_populates="user")
class UserAddress(Base):
__tablename__ = "user_addresses"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), index=True)
flat = Column(String)
building = Column(String)
landmark = Column(String)
lat = Column(Float)
lng = Column(Float)
address_type = Column(String) # HOME / WORK / OTHER
is_default = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.utcnow)
frontend_ip = Column(String, nullable=True)
backend_ip = Column(String, nullable=True)
user = relationship("User", back_populates="addresses")
# =========================
# VENDOR (Kitchen)
# =========================
class Vendor(Base):
__tablename__ = "vendors"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, nullable=False)
# 🔥 FULL ADDRESS (structured)
flat = Column(String)
building = Column(String)
landmark = Column(String)
location = Column(String) # full readable address
lat = Column(Float, nullable=False)
lng = Column(Float, nullable=False)
radius = Column(Float, default=2)
email = Column(String, unique=True)
password = Column(String)
is_active = Column(Boolean, default=True)
is_open = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.utcnow)
frontend_ip = Column(String, nullable=True)
backend_ip = Column(String, nullable=True)
city = Column(String)
zone = Column(String)
zonal_admin_id = Column(Integer, ForeignKey("admins.id"))
menus = relationship("VendorMenu", back_populates="vendor")
# =========================
# MENU ITEMS (Admin Controlled)
# =========================
class MenuItem(Base):
__tablename__ = "menu_items"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, nullable=False)
category = Column(String)
description = Column(Text) # 🔥 NEW
vendor_price = Column(Float, nullable=False)
selling_price = Column(Float, nullable=False)
image_url = Column(String, nullable=True)
is_deleted = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.utcnow)
vendors = relationship("VendorMenu", back_populates="menu_item")
# =========================
# VENDOR MENU (Availability)
# =========================
class VendorMenu(Base):
__tablename__ = "vendor_menu"
id = Column(Integer, primary_key=True, index=True)
vendor_id = Column(Integer, ForeignKey("vendors.id"), index=True)
menu_item_id = Column(Integer, ForeignKey("menu_items.id"), index=True)
is_available = Column(Boolean, default=True)
vendor = relationship("Vendor", back_populates="menus")
menu_item = relationship("MenuItem", back_populates="vendors")
# =========================
# ORDER
# =========================
class Order(Base):
__tablename__ = "orders"
id = Column(Integer, primary_key=True, index=True)
batch_id = Column(Integer, ForeignKey("batches.id"), nullable=True, index=True)
zonal_admin_id = Column(Integer, ForeignKey("admins.id"), index=True) # ✅ ADD
admin = relationship("Admin")
user_id = Column(Integer, ForeignKey("users.id"), index=True)
user_lat = Column(Float)
user_lng = Column(Float)
# 🔥 ORDER ADDRESS SNAPSHOT
address_flat = Column(String, nullable=True)
address_building = Column(String, nullable=True)
address_landmark = Column(String, nullable=True)
address_type = Column(String, nullable=True)
address_lat = Column(Float, nullable=True)
address_lng = Column(Float, nullable=True)
total_amount = Column(Float, nullable=False)
gst = Column(Float, nullable=False)
final_amount = Column(Float, nullable=False)
status = Column(String, default="PLACED", index=True)
reject_reason = Column(String, nullable=True) # 🔥 ADD THIS
payment_status = Column(String, default="PENDING")
delivery = relationship("Delivery", back_populates="order", uselist=False)
created_at = Column(DateTime, default=datetime.utcnow)
frontend_ip = Column(String, nullable=True)
backend_ip = Column(String, nullable=True)
user = relationship("User", back_populates="orders")
items = relationship("OrderItem", back_populates="order")
batch = relationship("Batch", back_populates="orders")
cancel_reason = Column(Text, nullable=True)
cancelled_by = Column(String, nullable=True)
cancelled_at = Column(DateTime, nullable=True)
# =========================
# ORDER ITEMS (Snapshot)
# =========================
class OrderItem(Base):
__tablename__ = "order_items"
id = Column(Integer, primary_key=True, index=True)
order_id = Column(Integer, ForeignKey("orders.id"), index=True)
menu_item_id = Column(Integer, ForeignKey("menu_items.id"), index=True)
vendor_id = Column(Integer, index=True)
quantity = Column(Integer, nullable=False)
name = Column(String)
vendor_price = Column(Float, nullable=False)
selling_price = Column(Float, nullable=False)
status = Column(String, default="PREPARING")
order = relationship("Order", back_populates="items")
# ✅ MUST BE INSIDE CLASS
__table_args__ = (
Index("idx_order_vendor", "order_id", "vendor_id"),
)
# =========================
# DELIVERY
# =========================
class Delivery(Base):
__tablename__ = "deliveries"
id = Column(Integer, primary_key=True, index=True)
order_id = Column(Integer, ForeignKey("orders.id"), index=True)
delivery_boy_id = Column(Integer, ForeignKey("delivery_boys.id"), index=True)
status = Column(String, default="ASSIGNED")
# Payment handling
payment_method = Column(String) # COD / QR
payment_amount = Column(Float) # ✅ store actual paid amount
payment_proof = Column(Text) # image URL / text note
created_at = Column(DateTime, default=datetime.utcnow)
frontend_ip = Column(String, nullable=True)
backend_ip = Column(String, nullable=True)
delivery_boy = relationship("DeliveryBoy")
order = relationship("Order", back_populates="delivery")
class Batch(Base):
__tablename__ = "batches"
id = Column(Integer, primary_key=True, index=True)
zonal_admin_id = Column(Integer, ForeignKey("admins.id"), index=True) # ✅ ADD
status = Column(String, default="CREATED", index=True)
# CREATED / ASSIGNED / PICKED_UP / OUT_FOR_DELIVERY / DELIVERED # CREATED / ASSIGNED / PICKED / COMPLETED
created_at = Column(DateTime, default=datetime.utcnow)
delivery_boy_id = Column(Integer, ForeignKey("delivery_boys.id"), nullable=True)
orders = relationship("Order", back_populates="batch")
# ✅ FIX HERE
delivery_boy = relationship(
"DeliveryBoy",
back_populates="batches",
foreign_keys=[delivery_boy_id]
)
# =========================
# ADMIN (City + Zonal)
# =========================
class Admin(Base):
__tablename__ = "admins"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, nullable=False)
email = Column(String, unique=True, nullable=False)
password = Column(String, nullable=False)
role = Column(String, nullable=False)
# CITY_ADMIN / ZONAL_ADMIN
city = Column(String, nullable=True)
zone = Column(String, nullable=True)
created_at = Column(DateTime, default=datetime.utcnow)
frontend_ip = Column(String, nullable=True)
backend_ip = Column(String, nullable=True)
class DeliveryBoy(Base):
__tablename__ = "delivery_boys"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, nullable=False)
email = Column(String, unique=True, nullable=False)
phone = Column(String, nullable=False)
password = Column(String, nullable=False)
city = Column(String)
zone = Column(String)
zonal_admin_id = Column(Integer, ForeignKey("admins.id"))
is_active = Column(Boolean, default=True)
# 🔥 NEW: STATUS SYSTEM
status = Column(String, default="OFFLINE", index=True)
# OFFLINE / IDLE / ASSIGNED / PICKED_UP / OUT_FOR_DELIVERY
# 🔥 NEW: ASSIGNMENT TRACKING (VERY IMPORTANT)
current_batch_id = Column(Integer, nullable=True)
current_order_id = Column(Integer, nullable=True)
# 🔥 NEW: LIVE LOCATION (for admin tracking later)
lat = Column(Float, nullable=True)
lng = Column(Float, nullable=True)
created_at = Column(DateTime, default=datetime.utcnow)
frontend_ip = Column(String, nullable=True)
backend_ip = Column(String, nullable=True)
# ✅ ADD THIS (CRITICAL FIX)
batches = relationship(
"Batch",
back_populates="delivery_boy",
foreign_keys="Batch.delivery_boy_id"
)
from sqlalchemy import Column, Integer, String, Text, DateTime, Boolean
from datetime import datetime
class NotificationToken(Base):
__tablename__ = "notification_tokens"
id = Column(
Integer,
primary_key=True,
index=True
)
user_id = Column(
Integer,
nullable=False,
index=True
)
role = Column(
String,
nullable=False
)
token = Column(
Text,
unique=True,
nullable=False
)
# ✅ DEVICE INFO
device_name = Column(String)
platform = Column(String)
browser = Column(String)
# ✅ ACTIVE / LOGOUT SUPPORT
is_active = Column(
Boolean,
default=True
)
created_at = Column(
DateTime,
default=datetime.utcnow
)
updated_at = Column(
DateTime,
default=datetime.utcnow,
onupdate=datetime.utcnow
)
# =========================
# REVIEWS
# =========================
class Review(Base):
__tablename__ = "reviews"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), index=True)
order_id = Column(Integer, ForeignKey("orders.id"), unique=True, index=True) # One review per order
zonal_admin_id = Column(Integer, ForeignKey("admins.id"), index=True)
rating = Column(Integer, nullable=False) # 1 to 5 stars
comment = Column(Text, nullable=True) # Optional written review
created_at = Column(DateTime, default=datetime.utcnow)
# Relationships
user = relationship("User")
order = relationship("Order")
zonal_admin = relationship("Admin")
# =========================
# SUPPORT TICKETS
# =========================
class SupportTicket(Base):
__tablename__ = "support_tickets"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"))
user_name = Column(String, nullable=False)
mobile_number = Column(String, nullable=False)
message = Column(Text, nullable=False)
status = Column(String, default="OPEN")
# OPEN / CLOSED / IN_PROGRESS
created_at = Column(DateTime, default=datetime.utcnow)
frontend_ip = Column(String, nullable=True)
backend_ip = Column(String, nullable=True)
user = relationship("User")
+48
View File
@@ -0,0 +1,48 @@
aiohappyeyeballs==2.6.1
aiohttp==3.13.1
aiosignal==1.4.0
annotated-types==0.7.0
anyio==4.10.0
attrs==25.4.0
bcrypt==3.2.2
certifi==2025.8.3
cffi==2.0.0
charset-normalizer==3.4.3
click==8.2.1
colorama==0.4.6
cryptography==45.0.7
ecdsa==0.19.1
fastapi==0.116.1
frozenlist==1.8.0
greenlet==3.2.4
h11==0.16.0
http_ece==1.2.1
httptools==0.6.4
idna==3.10
multidict==6.7.0
passlib==1.7.4
propcache==0.4.1
psycopg2-binary==2.9.10
py-vapid==1.9.2
pyasn1==0.6.1
pycparser==2.23
pydantic==2.11.7
pydantic_core==2.33.2
python-dotenv==1.1.1
python-jose==3.5.0
python-multipart==0.0.20
pywebpush==2.1.1
PyYAML==6.0.2
requests==2.32.5
rsa==4.9.1
six==1.17.0
sniffio==1.3.1
SQLAlchemy==2.0.43
starlette==0.47.3
typing-inspection==0.4.1
typing_extensions==4.15.0
urllib3==2.5.0
uvicorn==0.35.0
watchfiles==1.1.0
websockets==15.0.1
yarl==1.22.0
+648
View File
@@ -0,0 +1,648 @@
{
"name": "backend",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"@mui/x-date-pickers": "^9.1.0",
"dayjs": "^1.11.20",
"qrcode.react": "^4.2.0"
}
},
"node_modules/@babel/runtime": {
"version": "7.29.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
"integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@emotion/cache": {
"version": "11.14.0",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
"integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@emotion/memoize": "^0.9.0",
"@emotion/sheet": "^1.4.0",
"@emotion/utils": "^1.4.2",
"@emotion/weak-memoize": "^0.4.0",
"stylis": "4.2.0"
}
},
"node_modules/@emotion/hash": {
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
"integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
"license": "MIT",
"peer": true
},
"node_modules/@emotion/memoize": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
"license": "MIT",
"peer": true
},
"node_modules/@emotion/serialize": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
"integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@emotion/hash": "^0.9.2",
"@emotion/memoize": "^0.9.0",
"@emotion/unitless": "^0.10.0",
"@emotion/utils": "^1.4.2",
"csstype": "^3.0.2"
}
},
"node_modules/@emotion/sheet": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
"integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
"license": "MIT",
"peer": true
},
"node_modules/@emotion/unitless": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
"integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
"license": "MIT",
"peer": true
},
"node_modules/@emotion/utils": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
"integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
"license": "MIT",
"peer": true
},
"node_modules/@emotion/weak-memoize": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
"license": "MIT",
"peer": true
},
"node_modules/@mui/core-downloads-tracker": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-9.0.1.tgz",
"integrity": "sha512-GzamIIhZ1bH77dq7eKaeyRgJdkypsxin4jBFq2EMs4lBWRR0LFO1CSVMsoebn/VvjcNrnrOrjy48MkrkQUK2iw==",
"license": "MIT",
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
}
},
"node_modules/@mui/material": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-9.0.1.tgz",
"integrity": "sha512-voyCpeUxcSWLN7KPZuq0pGCIt726T9K6kiVM3XUcywZDAlZSarLHaUxJVQpospbjjOzN53hwyjo8s6KoWl6utw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.29.2",
"@mui/core-downloads-tracker": "^9.0.1",
"@mui/system": "^9.0.1",
"@mui/types": "^9.0.0",
"@mui/utils": "^9.0.1",
"@popperjs/core": "^2.11.8",
"@types/react-transition-group": "^4.4.12",
"clsx": "^2.1.1",
"csstype": "^3.2.3",
"prop-types": "^15.8.1",
"react-is": "^19.2.4",
"react-transition-group": "^4.4.5"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@emotion/react": "^11.5.0",
"@emotion/styled": "^11.3.0",
"@mui/material-pigment-css": "^9.0.1",
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
"optional": true
},
"@emotion/styled": {
"optional": true
},
"@mui/material-pigment-css": {
"optional": true
},
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/private-theming": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-9.0.1.tgz",
"integrity": "sha512-pSIGq4Yw749KHEwlkYZWVERgHgwJELP6ODtBNUfV8V4oIb5H+h7IQDFXuk/b2oQccODK1enJAtiEzlgLZmq+8g==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.29.2",
"@mui/utils": "^9.0.1",
"prop-types": "^15.8.1"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/styled-engine": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-9.0.0.tgz",
"integrity": "sha512-9RLGdX4Jg0aQPRuvqh/OLzYSPlgd5zyEw5/1HIRfdavSiOd03WtUaGZH9/w1RoTYuRKwpgy0hpIFaMHIqPVIWg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.29.2",
"@emotion/cache": "^11.14.0",
"@emotion/serialize": "^1.3.3",
"@emotion/sheet": "^1.4.0",
"csstype": "^3.2.3",
"prop-types": "^15.8.1"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@emotion/react": "^11.4.1",
"@emotion/styled": "^11.3.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
"optional": true
},
"@emotion/styled": {
"optional": true
}
}
},
"node_modules/@mui/system": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-9.0.1.tgz",
"integrity": "sha512-WvlioaLxk6ewUIOfh0StxUvOPDS1mCfzaulcudsL1brZNXuh0N9FMk7RpH7ImJKjEz412SEy/V/yvqmtxbqxCQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.29.2",
"@mui/private-theming": "^9.0.1",
"@mui/styled-engine": "^9.0.0",
"@mui/types": "^9.0.0",
"@mui/utils": "^9.0.1",
"clsx": "^2.1.1",
"csstype": "^3.2.3",
"prop-types": "^15.8.1"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@emotion/react": "^11.5.0",
"@emotion/styled": "^11.3.0",
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
"optional": true
},
"@emotion/styled": {
"optional": true
},
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/types": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@mui/types/-/types-9.0.0.tgz",
"integrity": "sha512-i1cuFCAWN44b3AJWO7mh7tuh1sqbQSeVr/94oG0TX5uXivac8XalgE4/6fQZcmGZigzbQ35IXxj/4jLpRIBYZg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.29.2"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/utils": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-9.0.1.tgz",
"integrity": "sha512-f3UO3jNN1pYg5zxqXC81Bvv8hx5ACcYc0387382ZI7M5ono1heIwHYLrKsz85myguWdeVKPRZGmDdynWUBjK2g==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.29.2",
"@mui/types": "^9.0.0",
"@types/prop-types": "^15.7.15",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"react-is": "^19.2.4"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/x-date-pickers": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-9.1.0.tgz",
"integrity": "sha512-vE2oXP8bAlwppFckOc4HEwbhj5Mz7ZUqKU8kNyDa6v19cYsX3ais+fcuCGMh1xZiO1Q+H97s9xgN3WzzgcfmPw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.29.2",
"@mui/utils": "9.0.0",
"@mui/x-internals": "^9.1.0",
"@types/react-transition-group": "^4.4.12",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"react-transition-group": "^4.4.5"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/material": "^7.3.0 || ^9.0.0",
"@mui/system": "^7.3.0 || ^9.0.0",
"date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0",
"date-fns-jalali": "^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0",
"dayjs": "^1.10.7",
"luxon": "^3.0.2",
"moment": "^2.29.4",
"moment-hijri": "^2.1.2 || ^3.0.0",
"moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
"optional": true
},
"@emotion/styled": {
"optional": true
},
"date-fns": {
"optional": true
},
"date-fns-jalali": {
"optional": true
},
"dayjs": {
"optional": true
},
"luxon": {
"optional": true
},
"moment": {
"optional": true
},
"moment-hijri": {
"optional": true
},
"moment-jalaali": {
"optional": true
}
}
},
"node_modules/@mui/x-date-pickers/node_modules/@mui/utils": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-9.0.0.tgz",
"integrity": "sha512-bQcqyg/gjULUqTuyUjSAFr6LQGLvtkNtDbJerAtoUn9kGZ0hg5QJiN1PLHMLbeFpe3te1831uq7GFl2ITokGdg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.29.2",
"@mui/types": "^9.0.0",
"@types/prop-types": "^15.7.15",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"react-is": "^19.2.4"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/x-internals": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-9.1.0.tgz",
"integrity": "sha512-fVezTa1lU+Hb3y9UMI8D/iWXADhs0I8PaZqoh2LOUXjGEUJmKqwsRD19ZXInZsH2yu+YS0dqYMPDvzjYTTyo+Q==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.29.2",
"@mui/utils": "9.0.0",
"reselect": "^5.1.1",
"use-sync-external-store": "^1.6.0"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/@mui/x-internals/node_modules/@mui/utils": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-9.0.0.tgz",
"integrity": "sha512-bQcqyg/gjULUqTuyUjSAFr6LQGLvtkNtDbJerAtoUn9kGZ0hg5QJiN1PLHMLbeFpe3te1831uq7GFl2ITokGdg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.29.2",
"@mui/types": "^9.0.0",
"@types/prop-types": "^15.7.15",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"react-is": "^19.2.4"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"license": "MIT",
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@types/prop-types": {
"version": "15.7.15",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
"integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
"license": "MIT"
},
"node_modules/@types/react": {
"version": "19.2.14",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
},
"node_modules/@types/react-transition-group": {
"version": "4.4.12",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
"integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*"
}
},
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/csstype": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"license": "MIT"
},
"node_modules/dayjs": {
"version": "1.11.20",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz",
"integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==",
"license": "MIT"
},
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"license": "MIT"
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"license": "MIT",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"node_modules/prop-types/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"license": "MIT"
},
"node_modules/qrcode.react": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.2.0.tgz",
"integrity": "sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==",
"license": "ISC",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/react": {
"version": "19.2.6",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz",
"integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
"version": "19.2.6",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz",
"integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
"peerDependencies": {
"react": "^19.2.6"
}
},
"node_modules/react-is": {
"version": "19.2.6",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.6.tgz",
"integrity": "sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==",
"license": "MIT"
},
"node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"license": "BSD-3-Clause",
"dependencies": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
},
"peerDependencies": {
"react": ">=16.6.0",
"react-dom": ">=16.6.0"
}
},
"node_modules/reselect": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
"license": "MIT"
},
"node_modules/scheduler": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
"license": "MIT",
"peer": true
},
"node_modules/stylis": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
"license": "MIT",
"peer": true
},
"node_modules/use-sync-external-store": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
}
}
}
+7
View File
@@ -0,0 +1,7 @@
{
"dependencies": {
"@mui/x-date-pickers": "^9.1.0",
"dayjs": "^1.11.20",
"qrcode.react": "^4.2.0"
}
}
BIN
View File
Binary file not shown.
+48
View File
@@ -0,0 +1,48 @@
aiohappyeyeballs==2.6.1
aiohttp==3.13.1
aiosignal==1.4.0
annotated-types==0.7.0
anyio==4.10.0
attrs==25.4.0
bcrypt==3.2.2
certifi==2025.8.3
cffi==2.0.0
charset-normalizer==3.4.3
click==8.2.1
colorama==0.4.6
cryptography==45.0.7
ecdsa==0.19.1
fastapi==0.116.1
frozenlist==1.8.0
greenlet==3.2.4
h11==0.16.0
http_ece==1.2.1
httptools==0.6.4
idna==3.10
multidict==6.7.0
passlib==1.7.4
propcache==0.4.1
psycopg2-binary==2.9.10
py-vapid==1.9.2
pyasn1==0.6.1
pycparser==2.23
pydantic==2.11.7
pydantic_core==2.33.2
python-dotenv==1.1.1
python-jose==3.5.0
python-multipart==0.0.20
pywebpush==2.1.1
PyYAML==6.0.2
requests==2.32.5
rsa==4.9.1
six==1.17.0
sniffio==1.3.1
SQLAlchemy==2.0.43
starlette==0.47.3
typing-inspection==0.4.1
typing_extensions==4.15.0
urllib3==2.5.0
uvicorn==0.35.0
watchfiles==1.1.0
websockets==15.0.1
yarl==1.22.0
+120
View File
@@ -0,0 +1,120 @@
from pydantic import BaseModel
from typing import List
from typing import Literal
class AddressCreate(BaseModel):
user_id: int
flat: str
building: str
landmark: str = None
lat: float
lng: float
address_type: Literal["HOME", "WORK", "OTHER"] = "HOME"
frontend_ip: str
class OrderItemRequest(BaseModel):
menu_item_id: int
quantity: int
vendor_id: int
class OrderCreate(BaseModel):
frontend_ip: str | None = None
user_id: int
lat: float
lng: float
items: List[OrderItemRequest]
class AssignDelivery(BaseModel):
order_id: int
delivery_boy_id: int
class UpdateDeliveryStatus(BaseModel):
order_id: int
status: str # ASSIGNED / PICKED / DELIVERED
class DeliveryPayment(BaseModel):
order_id: int
payment_method: str
payment_proof: str | None = None
amount: float
# User Login
class UserCreate(BaseModel):
name: str
phone: str
email: str
class SendOTP(BaseModel):
phone: str
class VerifyOTP(BaseModel):
phone: str
otp: str
name: str = None
email: str = None
# ✅ NEW
frontend_ip: str | None = None
# ================= ADMIN =================
class AdminLogin(BaseModel):
email: str
password: str
class CreateZonalAdmin(BaseModel):
name: str
email: str
password: str
zone: str
class VendorCreate(BaseModel):
name: str
location: str
flat: str = None
building: str = None
landmark: str = None
lat: float | None = None
lng: float | None = None
email: str
password: str
class DeliveryBoyCreate(BaseModel):
name: str
email: str
phone: str
password: str
class DeliveryBoyLogin(BaseModel):
email: str
password: str
frontend_ip: str | None = None
class ReviewCreate(BaseModel):
user_id: int
order_id: int
rating: int
comment: str | None = None
# =========================
# SUPPORT TICKET
# =========================
class SupportTicketCreate(BaseModel):
user_id: int
message: str
frontend_ip: str | None = None
+76
View File
@@ -0,0 +1,76 @@
import math
from fastapi import HTTPException
def haversine(lat1, lon1, lat2, lon2):
R = 6371 # Earth radius in KM
dLat = math.radians(lat2 - lat1)
dLon = math.radians(lon2 - lon1)
a = (
math.sin(dLat / 2) ** 2 +
math.cos(math.radians(lat1)) *
math.cos(math.radians(lat2)) *
math.sin(dLon / 2) ** 2
)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
return R * c
def calculate_eta(distance):
prep_time = 10 # mins
travel_time = distance * 5 # mins per km
eta = prep_time + travel_time
return f"{int(eta)}-{int(eta + 5)} mins"
# ================= AUTH =================
from passlib.context import CryptContext
from jose import jwt
from datetime import datetime, timedelta
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
SECRET_KEY = "secret123"
ALGORITHM = "HS256"
def hash_password(password: str):
password = password[:72] # 🔥 FIX
return pwd_context.hash(password)
def verify_password(plain, hashed):
plain = plain[:72]
return pwd_context.verify(plain, hashed)
def create_token(data: dict):
to_encode = data.copy()
to_encode["exp"] = datetime.utcnow() + timedelta(days=30)
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
# Admin
from fastapi import Depends
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
security = HTTPBearer()
def get_current_admin(
credentials: HTTPAuthorizationCredentials = Depends(security)
):
try:
payload = jwt.decode(
credentials.credentials,
SECRET_KEY,
algorithms=[ALGORITHM]
)
return payload
except Exception as e:
raise HTTPException(
status_code=401,
detail="Invalid token"
)