"""
myapp_motor, a minimal example using Motor (MongoDB) as the session backend
with MicroPie.
This application increments a visit counter stored in a MongoDB collection for sessions.
"""
from micropie import App, SessionBackend, SESSION_TIMEOUT
import motor.motor_asyncio
import uuid
from datetime import datetime, timedelta
from typing import Dict, Any
class MotorSessionBackend(SessionBackend):
def __init__(
self,
mongo_uri: str,
db_name: str,
collection_name: str = "sessions",
default_timeout: int = SESSION_TIMEOUT,
) -> None:
"""
A simple MongoDB-backed session store for MicroPie using Motor.
NOTE:
- Suitable for real deployments as a starting point.
- You should still tune indexes, replica set, etc. for your environment.
"""
self.client = motor.motor_asyncio.AsyncIOMotorClient(mongo_uri)
self.db = self.client[db_name]
self.collection = self.db[collection_name]
self.default_timeout = default_timeout
async def load(self, session_id: str) -> Dict[str, Any]:
"""
Load session data from MongoDB.
- If no session_id is provided, returns an empty dict.
- If the session is expired, deletes it and returns empty.
"""
if not session_id:
return {}
doc = await self.collection.find_one({"_id": session_id})
if not doc:
return {}
expires_at = doc.get("expires_at")
if expires_at is not None and datetime.utcnow() > expires_at:
# Expired: clean up and treat as no session
await self.collection.delete_one({"_id": session_id})
return {}
return doc.get("data", {}) or {}
async def save(self, session_id: str, data: Dict[str, Any], timeout: int) -> None:
"""
Save session data into MongoDB with an expiration time.
MicroPie may call this with:
- `data` empty and/or `timeout <= 0` to mean "delete this session".
"""
if not session_id:
# If we somehow got here with an empty session_id, just ignore.
return
# Treat empty data or non-positive timeout as a delete / logout
if not data or timeout <= 0:
await self.collection.delete_one({"_id": session_id})
return
# Use the timeout provided by MicroPie if set, otherwise our default.
ttl = timeout or self.default_timeout
expires_at = datetime.utcnow() + timedelta(seconds=ttl)
await self.collection.update_one(
{"_id": session_id},
{"$set": {"data": data, "expires_at": expires_at}},
upsert=True,
)
class MyApp(App):
async def index(self):
# Access the session via self.request.session.
if "visits" not in self.request.session:
self.request.session["visits"] = 1
else:
self.request.session["visits"] += 1
return f"You have visited {self.request.session['visits']} times."
# MongoDB configuration; adjust the URI and database name as needed.
MONGO_URI = "your uri here"
DB_NAME = "example"
# Create an instance of the Motor session backend.
backend = MotorSessionBackend(MONGO_URI, DB_NAME)
# Pass the Motor session backend to our application.
app = MyApp(session_backend=backend)