From dbaa7b0d8bd0399f82a73b897f9cbe2512fdc68a Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 28 Feb 2026 09:24:02 +0000 Subject: [PATCH 1/3] Replace lifespan with on_event handlers for older FastAPI compat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The lifespan constructor parameter was added in FastAPI 0.93. When running outside Docker with an older system-packaged FastAPI, the parameter is silently ignored — the app starts and serves routes but init_db() and start_presence() never run, so the bot never goes online and webhooks crash with "Database not initialised". on_event("startup")/on_event("shutdown") work across all FastAPI versions. https://claude.ai/code/session_019ANRdyL2jfi7ysWhqN4PfD --- app/main.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/app/main.py b/app/main.py index 4fdefad..27d3676 100644 --- a/app/main.py +++ b/app/main.py @@ -1,5 +1,4 @@ import logging -from contextlib import asynccontextmanager from fastapi import FastAPI @@ -13,18 +12,8 @@ logging.basicConfig( ) log = logging.getLogger(__name__) - -@asynccontextmanager -async def lifespan(app: FastAPI): - log.info("Starting Melora") - await init_db() - await _ensure_thread_roots() - await start_presence() - log.info("Melora ready") - yield - log.info("Shutting down Melora") - await close_client() - await close_db() +app = FastAPI(title="Melora") +app.include_router(webhook_router) async def _ensure_thread_roots() -> None: @@ -38,8 +27,20 @@ async def _ensure_thread_roots() -> None: await set_thread_root(media_type, event_id) -app = FastAPI(title="Melora", lifespan=lifespan) -app.include_router(webhook_router) +@app.on_event("startup") +async def startup() -> None: + log.info("Starting Melora") + await init_db() + await _ensure_thread_roots() + await start_presence() + log.info("Melora ready") + + +@app.on_event("shutdown") +async def shutdown() -> None: + log.info("Shutting down Melora") + await close_client() + await close_db() @app.get("/health") From 7cc5fcb514e85e0a35187e33af7d8b0e63a2c832 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 28 Feb 2026 09:27:40 +0000 Subject: [PATCH 2/3] Revert to lifespan handler, use venv for correct FastAPI version https://claude.ai/code/session_019ANRdyL2jfi7ysWhqN4PfD --- app/main.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/app/main.py b/app/main.py index 27d3676..4fdefad 100644 --- a/app/main.py +++ b/app/main.py @@ -1,4 +1,5 @@ import logging +from contextlib import asynccontextmanager from fastapi import FastAPI @@ -12,8 +13,18 @@ logging.basicConfig( ) log = logging.getLogger(__name__) -app = FastAPI(title="Melora") -app.include_router(webhook_router) + +@asynccontextmanager +async def lifespan(app: FastAPI): + log.info("Starting Melora") + await init_db() + await _ensure_thread_roots() + await start_presence() + log.info("Melora ready") + yield + log.info("Shutting down Melora") + await close_client() + await close_db() async def _ensure_thread_roots() -> None: @@ -27,20 +38,8 @@ async def _ensure_thread_roots() -> None: await set_thread_root(media_type, event_id) -@app.on_event("startup") -async def startup() -> None: - log.info("Starting Melora") - await init_db() - await _ensure_thread_roots() - await start_presence() - log.info("Melora ready") - - -@app.on_event("shutdown") -async def shutdown() -> None: - log.info("Shutting down Melora") - await close_client() - await close_db() +app = FastAPI(title="Melora", lifespan=lifespan) +app.include_router(webhook_router) @app.get("/health") From e4c696d142d90fc99410221e05665f92e8bc77be Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 28 Feb 2026 09:31:39 +0000 Subject: [PATCH 3/3] Add venv setup instructions to README https://claude.ai/code/session_019ANRdyL2jfi7ysWhqN4PfD --- README.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 34a3681..410bee0 100644 --- a/README.md +++ b/README.md @@ -30,22 +30,30 @@ cp .env.example .env Edit `.env` with your actual values (see [Environment Variables](#environment-variables) below). -### 2. Verify your configuration +### 2. Set up a virtual environment ```bash +python3 -m venv .venv +source .venv/bin/activate pip install -r requirements.txt +``` + +### 3. Verify your configuration + +```bash python -m app --check ``` This validates that all required environment variables are set, the Matrix homeserver is reachable, the bot token is valid, the bot has joined the announcements room, and the database path is writable. Fix any failing checks before starting the server. -### 3. Run locally +### 4. Run locally ```bash +source .venv/bin/activate uvicorn app.main:app --host 0.0.0.0 --port 8000 ``` -### 4. Run with Docker +### 5. Run with Docker ```bash docker build -t melora .