1
0
mirror of https://github.com/aquatix/digimarks.git synced 2025-12-07 18:55:11 +01:00

Application now uses async DB session everywhere

This commit is contained in:
2025-09-12 12:03:12 +02:00
parent 1219371185
commit 3a87485b9a

View File

@@ -20,7 +20,10 @@ from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
from pydantic import AnyUrl, DirectoryPath, FilePath, computed_field from pydantic import AnyUrl, DirectoryPath, FilePath, computed_field
from pydantic_settings import BaseSettings from pydantic_settings import BaseSettings
from sqlmodel import AutoString, Field, Session, SQLModel, create_engine, desc, select from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.orm import sessionmaker
from sqlmodel import AutoString, Field, SQLModel, desc, select
from sqlmodel.ext.asyncio.session import AsyncSession
DIGIMARKS_USER_AGENT = 'digimarks/2.0.0-dev' DIGIMARKS_USER_AGENT = 'digimarks/2.0.0-dev'
DIGIMARKS_VERSION = '2.0.0a1' DIGIMARKS_VERSION = '2.0.0a1'
@@ -49,16 +52,17 @@ class Settings(BaseSettings):
settings = Settings() settings = Settings()
print(settings.model_dump()) print(settings.model_dump())
engine = create_engine(f'sqlite:///{settings.database_file}', connect_args={'check_same_thread': False}) engine = create_async_engine(f'sqlite+aiosqlite:///{settings.database_file}', connect_args={'check_same_thread': False})
def get_session(): async def get_session() -> AsyncSession:
"""SQLAlchemy session factory.""" """SQLAlchemy session factory."""
with Session(engine) as session: async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async with async_session() as session:
yield session yield session
SessionDep = Annotated[Session, Depends(get_session)] SessionDep = Annotated[AsyncSession, Depends(get_session)]
@asynccontextmanager @asynccontextmanager
@@ -358,7 +362,7 @@ def index(request: Request):
@app.get('/api/v1/admin/{system_key}/users/{user_id}', response_model=User) @app.get('/api/v1/admin/{system_key}/users/{user_id}', response_model=User)
def get_user(session: SessionDep, system_key: str, user_id: int) -> Type[User]: async def get_user(session: SessionDep, system_key: str, user_id: int) -> Type[User]:
"""Show user information.""" """Show user information."""
if system_key != settings.system_key: if system_key != settings.system_key:
raise HTTPException(status_code=404) raise HTTPException(status_code=404)
@@ -371,7 +375,7 @@ def get_user(session: SessionDep, system_key: str, user_id: int) -> Type[User]:
# @app.get('/admin/{system_key}/users/', response_model=list[User]) # @app.get('/admin/{system_key}/users/', response_model=list[User])
@app.get('/api/v1/admin/{system_key}/users/') @app.get('/api/v1/admin/{system_key}/users/')
def list_users( async def list_users(
session: SessionDep, session: SessionDep,
system_key: str, system_key: str,
offset: int = 0, offset: int = 0,
@@ -394,40 +398,42 @@ def list_users(
@app.get('/api/v1/{user_key}/bookmarks/') @app.get('/api/v1/{user_key}/bookmarks/')
def list_bookmarks( async def list_bookmarks(
session: SessionDep, session: SessionDep,
user_key: str, user_key: str,
offset: int = 0, offset: int = 0,
limit: Annotated[int, Query(le=10000)] = 100, limit: Annotated[int, Query(le=10000)] = 100,
) -> list[Bookmark]: ) -> list[Bookmark]:
"""List all bookmarks in the database. By default 100 items are returned.""" """List all bookmarks in the database. By default 100 items are returned."""
bookmarks = session.exec( result = await session.exec(
select(Bookmark) select(Bookmark)
.where(Bookmark.userkey == user_key, Bookmark.status != Visibility.DELETED) .where(Bookmark.userkey == user_key, Bookmark.status != Visibility.DELETED)
.offset(offset) .offset(offset)
.limit(limit) .limit(limit)
).all() )
bookmarks = result.all()
return bookmarks return bookmarks
@app.get('/api/v1/{user_key}/bookmarks/{url_hash}') @app.get('/api/v1/{user_key}/bookmarks/{url_hash}')
def get_bookmark( async def get_bookmark(
session: SessionDep, session: SessionDep,
user_key: str, user_key: str,
url_hash: str, url_hash: str,
) -> Bookmark: ) -> Bookmark:
"""Show bookmark details.""" """Show bookmark details."""
bookmark = session.exec( result = await session.exec(
select(Bookmark).where( select(Bookmark).where(
Bookmark.userkey == user_key, Bookmark.url_hash == url_hash, Bookmark.status != Visibility.DELETED Bookmark.userkey == user_key, Bookmark.url_hash == url_hash, Bookmark.status != Visibility.DELETED
) )
).first() )
bookmark = result.first()
# bookmark = session.get(Bookmark, {'url_hash': url_hash, 'userkey': user_key}) # bookmark = session.get(Bookmark, {'url_hash': url_hash, 'userkey': user_key})
return bookmark return bookmark
@app.post('/api/v1/{user_key}/autocomplete_bookmark/', response_model=Bookmark) @app.post('/api/v1/{user_key}/autocomplete_bookmark/', response_model=Bookmark)
def autocomplete_bookmark( async def autocomplete_bookmark(
session: SessionDep, session: SessionDep,
request: Request, request: Request,
user_key: str, user_key: str,
@@ -441,11 +447,12 @@ def autocomplete_bookmark(
update_bookmark_with_info(bookmark, request, strip_params) update_bookmark_with_info(bookmark, request, strip_params)
url_hash = generate_hash(str(bookmark.url)) url_hash = generate_hash(str(bookmark.url))
bookmark_db = session.exec( result = await session.exec(
select(Bookmark).where( select(Bookmark).where(
Bookmark.userkey == user_key, Bookmark.url_hash == url_hash, Bookmark.status != Visibility.DELETED Bookmark.userkey == user_key, Bookmark.url_hash == url_hash, Bookmark.status != Visibility.DELETED
) )
).first() )
bookmark_db = result.first()
if bookmark_db: if bookmark_db:
# Bookmark with this URL already exists, provide the hash so the frontend can look it up and the user can # Bookmark with this URL already exists, provide the hash so the frontend can look it up and the user can
# merge them if so wanted # merge them if so wanted
@@ -455,7 +462,7 @@ def autocomplete_bookmark(
@app.post('/api/v1/{user_key}/bookmarks/', response_model=Bookmark) @app.post('/api/v1/{user_key}/bookmarks/', response_model=Bookmark)
def add_bookmark( async def add_bookmark(
session: SessionDep, session: SessionDep,
request: Request, request: Request,
user_key: str, user_key: str,
@@ -470,13 +477,13 @@ def add_bookmark(
bookmark.url_hash = generate_hash(str(bookmark.url)) bookmark.url_hash = generate_hash(str(bookmark.url))
session.add(bookmark) session.add(bookmark)
session.commit() await session.commit()
session.refresh(bookmark) await session.refresh(bookmark)
return bookmark return bookmark
@app.patch('/api/v1/{user_key}/bookmarks/{url_hash}', response_model=Bookmark) @app.patch('/api/v1/{user_key}/bookmarks/{url_hash}', response_model=Bookmark)
def update_bookmark( async def update_bookmark(
session: SessionDep, session: SessionDep,
request: Request, request: Request,
user_key: str, user_key: str,
@@ -485,11 +492,12 @@ def update_bookmark(
strip_params: bool = False, strip_params: bool = False,
): ):
"""Update existing bookmark `bookmark_key` for user `user_key`.""" """Update existing bookmark `bookmark_key` for user `user_key`."""
bookmark_db = session.exec( result = await session.exec(
select(Bookmark).where( select(Bookmark).where(
Bookmark.userkey == user_key, Bookmark.url_hash == url_hash, Bookmark.status != Visibility.DELETED Bookmark.userkey == user_key, Bookmark.url_hash == url_hash, Bookmark.status != Visibility.DELETED
) )
).first() )
bookmark_db = result.first()
if not bookmark_db: if not bookmark_db:
raise HTTPException(status_code=404, detail='Bookmark not found') raise HTTPException(status_code=404, detail='Bookmark not found')
@@ -510,13 +518,14 @@ def update_bookmark(
@app.delete('/api/v1/{user_key}/bookmarks/{url_hash}', response_model=Bookmark) @app.delete('/api/v1/{user_key}/bookmarks/{url_hash}', response_model=Bookmark)
def delete_bookmark( async def delete_bookmark(
session: SessionDep, session: SessionDep,
user_key: str, user_key: str,
url_hash: str, url_hash: str,
): ):
"""(Soft)Delete bookmark `bookmark_key` for user `user_key`.""" """(Soft)Delete bookmark `bookmark_key` for user `user_key`."""
bookmark = session.get(Bookmark, {'url_hash': url_hash, 'userkey': user_key}) result = await session.get(Bookmark, {'url_hash': url_hash, 'userkey': user_key})
bookmark = result
if not bookmark: if not bookmark:
raise HTTPException(status_code=404, detail='Bookmark not found') raise HTTPException(status_code=404, detail='Bookmark not found')
bookmark.deleted_date = datetime.now(UTC) bookmark.deleted_date = datetime.now(UTC)
@@ -527,21 +536,23 @@ def delete_bookmark(
@app.get('/api/v1/{user_key}/latest_changes/') @app.get('/api/v1/{user_key}/latest_changes/')
def bookmarks_changed_since( async def bookmarks_changed_since(
session: SessionDep, session: SessionDep,
user_key: str, user_key: str,
): ):
"""Last update on server, so the (browser) client knows whether to fetch an update.""" """Last update on server, so the (browser) client knows whether to fetch an update."""
latest_modified_bookmark = session.exec( result = await session.exec(
select(Bookmark) select(Bookmark)
.where(Bookmark.userkey == user_key, Bookmark.status != Visibility.DELETED) .where(Bookmark.userkey == user_key, Bookmark.status != Visibility.DELETED)
.order_by(desc(Bookmark.modified_date)) .order_by(desc(Bookmark.modified_date))
).first() )
latest_created_bookmark = session.exec( latest_modified_bookmark = result.first()
result = await session.exec(
select(Bookmark) select(Bookmark)
.where(Bookmark.userkey == user_key, Bookmark.status != Visibility.DELETED) .where(Bookmark.userkey == user_key, Bookmark.status != Visibility.DELETED)
.order_by(desc(Bookmark.created_date)) .order_by(desc(Bookmark.created_date))
).first() )
latest_created_bookmark = result.first()
latest_modification = max(latest_modified_bookmark.modified_date, latest_created_bookmark.created_date) latest_modification = max(latest_modified_bookmark.modified_date, latest_created_bookmark.created_date)
@@ -554,14 +565,15 @@ def bookmarks_changed_since(
@app.get('/api/v1/{user_key}/tags/') @app.get('/api/v1/{user_key}/tags/')
def list_tags_for_user( async def list_tags_for_user(
session: SessionDep, session: SessionDep,
user_key: str, user_key: str,
) -> list[str]: ) -> list[str]:
"""List all tags in use by the user.""" """List all tags in use by the user."""
bookmarks = session.exec( result = await session.exec(
select(Bookmark).where(Bookmark.userkey == user_key, Bookmark.status != Visibility.DELETED) select(Bookmark).where(Bookmark.userkey == user_key, Bookmark.status != Visibility.DELETED)
).all() )
bookmarks = result.all()
tags = [] tags = []
for bookmark in bookmarks: for bookmark in bookmarks:
tags += bookmark.tag_list tags += bookmark.tag_list
@@ -569,23 +581,25 @@ def list_tags_for_user(
@app.get('/api/v1/{user_key}/tags/{tag_key}') @app.get('/api/v1/{user_key}/tags/{tag_key}')
def list_tags_for_user( async def list_tags_for_user(
session: SessionDep, session: SessionDep,
user_key: str, user_key: str,
) -> list[str]: ) -> list[str]:
"""List all tags in use by the user.""" """List all tags in use by the user."""
bookmarks = session.exec(select(Bookmark).where(Bookmark.userkey == user_key)).all() result = await session.exec(select(Bookmark).where(Bookmark.userkey == user_key))
bookmarks = result.all()
return list_tags_for_bookmarks(bookmarks) return list_tags_for_bookmarks(bookmarks)
@app.get('/{user_key}', response_class=HTMLResponse) @app.get('/{user_key}', response_class=HTMLResponse)
def page_user_landing( async def page_user_landing(
session: SessionDep, session: SessionDep,
request: Request, request: Request,
user_key: str, user_key: str,
): ):
"""HTML page with the main view for the user.""" """HTML page with the main view for the user."""
user = session.exec(select(User).where(User.key == user_key)).first() result = await session.exec(select(User).where(User.key == user_key))
user = result.first()
if not user: if not user:
raise HTTPException(status_code=404, detail='User not found') raise HTTPException(status_code=404, detail='User not found')
language = 'en' language = 'en'