فهرست منبع

sqlite: log clean error with db path on OperationalError, suppress stacktrace

ZiB 4 ماه پیش
والد
کامیت
6bd998b63e
2فایلهای تغییر یافته به همراه21 افزوده شده و 8 حذف شده
  1. 6 0
      room_cal/api/__init__.py
  2. 15 8
      room_cal/store/_sqlite.py

+ 6 - 0
room_cal/api/__init__.py

@@ -52,6 +52,7 @@ _log = __import__('logging').getLogger(__name__)
 
 
 def create_app() -> FastAPI:
+  import sqlite3
   from caldav.lib.error import AuthorizationError
 
   app = FastAPI(title='room_cal', lifespan=_lifespan)
@@ -61,6 +62,11 @@ def create_app() -> FastAPI:
     _log.error('caldav: %s', exc)
     return JSONResponse({'detail': str(exc)}, status_code=500)
 
+  @app.exception_handler(sqlite3.OperationalError)
+  async def _sqlite_error(request: Request, exc: sqlite3.OperationalError):
+    _log.error('sqlite: %s', exc)
+    return JSONResponse({'detail': str(exc)}, status_code=500)
+
   app.add_middleware(CORSMiddleware, allow_origins=['*'], allow_methods=['*'], allow_headers=['*'])
   app.add_middleware(_AuthMiddleware)
   app.include_router(auth_router)

+ 15 - 8
room_cal/store/_sqlite.py

@@ -1,4 +1,6 @@
 import logging
+import sqlite3
+from contextlib import asynccontextmanager
 from datetime import datetime
 from pathlib import Path
 
@@ -37,6 +39,15 @@ class SQLiteBackend:
       db_path = data_home() / db_path
     return cls(db_path, calendar_name)
 
+  @asynccontextmanager
+  async def _connect(self):
+    import aiosqlite
+    try:
+      async with aiosqlite.connect(self._db_path) as db:
+        yield db
+    except sqlite3.OperationalError as e:
+      raise sqlite3.OperationalError(f"SQLite error for {self._db_path}: {e}") from None
+
   def _to_record(self, row) -> EventRecord:
     uid, _, start, end, all_day, title, description, x_data = row
     return EventRecord(
@@ -50,8 +61,7 @@ class SQLiteBackend:
     )
 
   async def fetch_range(self, start: datetime, end: datetime) -> list[EventRecord]:
-    import aiosqlite
-    async with aiosqlite.connect(self._db_path) as db:
+    async with self._connect() as db:
       await db.execute(_CREATE_TABLE)
       cursor = await db.execute(
         f'SELECT {_COLS} FROM events WHERE calendar_name = ? AND start < ? AND end > ? ORDER BY start',
@@ -61,8 +71,7 @@ class SQLiteBackend:
     return [self._to_record(r) for r in rows]
 
   async def create(self, record: EventRecord) -> EventRecord:
-    import aiosqlite
-    async with aiosqlite.connect(self._db_path) as db:
+    async with self._connect() as db:
       await db.execute(_CREATE_TABLE)
       await db.execute(
         f'INSERT INTO events ({_COLS}) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
@@ -75,8 +84,7 @@ class SQLiteBackend:
 
   async def update(self, uid: str, *, title: str | None = None,
                    description: str | None = None, x_data: str | None = None) -> EventRecord:
-    import aiosqlite
-    async with aiosqlite.connect(self._db_path) as db:
+    async with self._connect() as db:
       await db.execute(_CREATE_TABLE)
       if title is not None:
         await db.execute(
@@ -103,8 +111,7 @@ class SQLiteBackend:
     return self._to_record(row)
 
   async def delete(self, uid: str) -> None:
-    import aiosqlite
-    async with aiosqlite.connect(self._db_path) as db:
+    async with self._connect() as db:
       await db.execute(_CREATE_TABLE)
       await db.execute(
         'DELETE FROM events WHERE uid = ? AND calendar_name = ?',