Spaces:
Configuration error
Configuration error
File size: 4,248 Bytes
6733714 | 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 | import logging
import os
from typing import Optional
from pymongo import MongoClient, ASCENDING, DESCENDING
from pymongo.errors import OperationFailure
logger = logging.getLogger(__name__)
# Supports both:
# Local dev : mongodb://localhost:27017
# Atlas : mongodb+srv://user:pass@cluster.mongodb.net/?retryWrites=true&w=majority
# Set MONGODB_URI (Atlas style) or fall back to MONGO_URI (legacy).
MONGO_URI = (
os.getenv("MONGODB_URI") # Atlas / production
or os.getenv("MONGO_URI") # legacy env var name
or "mongodb://localhost:27017" # local dev fallback
)
DB_NAME = os.getenv("MONGO_DB_NAME", "leta_history")
def _ensure_indexes(db) -> None:
"""
Creates indexes on first connect (idempotent — MongoDB ignores duplicates).
Without these, template search and session lookup are O(n) collection scans.
"""
try:
# users: fast login lookup by email or phone
db["users"].create_index([("email", ASCENDING)], unique=True, sparse=True)
db["users"].create_index([("phone", ASCENDING)], unique=True, sparse=True)
# sessions: fetch all sessions for a user quickly
db["sessions"].create_index([("user_id", ASCENDING)])
db["sessions"].create_index([("session_id", ASCENDING)], unique=True)
db["sessions"].create_index([("updated_at", DESCENDING)])
# sessions: auto-expire sessions inactive for 90 days
db["sessions"].create_index(
[("updated_at", ASCENDING)],
expireAfterSeconds=90 * 24 * 3600,
name="sessions_ttl",
)
# templates: domain + tag filtering (Netflix-style browse)
db["templates"].create_index([("domain", ASCENDING), ("tags", ASCENDING)])
db["templates"].create_index([("title", ASCENDING)])
# otp_store: TTL index — MongoDB auto-deletes expired OTPs
db["otp_store"].create_index(
[("expires_at", ASCENDING)],
expireAfterSeconds=0, # delete at the document's own expires_at
)
db["otp_store"].create_index([("contact", ASCENDING)], unique=True)
# feedback: analytics queries by rating and timestamp
db["feedback"].create_index([("rating", ASCENDING), ("timestamp", DESCENDING)])
logger.info("MongoDB indexes ensured")
except OperationFailure as e:
# Non-fatal: Atlas free tier may restrict index creation
logger.warning(f"Index creation warning (non-fatal): {e}")
class Database:
client: Optional[MongoClient] = None
db_name: str = DB_NAME
def connect(self):
if not self.client:
# Mask credentials in log output
safe_uri = MONGO_URI.split("@")[-1] if "@" in MONGO_URI else MONGO_URI
logger.info(f"Connecting to MongoDB: ...{safe_uri}")
try:
self.client = MongoClient(
MONGO_URI,
serverSelectionTimeoutMS=5000,
connectTimeoutMS=10000,
socketTimeoutMS=30000,
minPoolSize=5,
maxPoolSize=50,
maxIdleTimeMS=45000,
tls=MONGO_URI.startswith("mongodb+srv"),
retryWrites=True,
w="majority",
)
self.client.admin.command("ping")
logger.info("MongoDB connection successful")
_ensure_indexes(self.client[self.db_name])
except Exception as e:
logger.error(f"MongoDB connection failed: {e}")
self.client = None
def get_collection(self, collection_name: str):
if not self.client:
self.connect()
if self.client:
return self.client[self.db_name][collection_name]
return None
db = Database()
def get_session_collection():
return db.get_collection("sessions")
def get_user_collection():
return db.get_collection("users")
def get_template_collection():
return db.get_collection("templates")
def get_otp_collection():
return db.get_collection("otp_store")
def get_db():
if not db.client:
db.connect()
if db.client:
return db.client[db.db_name]
return None
|