|
|
|
|
@@ -4,7 +4,7 @@ import logging
|
|
|
|
|
from collections.abc import Sequence
|
|
|
|
|
from contextlib import asynccontextmanager
|
|
|
|
|
from datetime import UTC, datetime
|
|
|
|
|
from typing import Annotated
|
|
|
|
|
from typing import Annotated, AsyncGenerator, cast
|
|
|
|
|
|
|
|
|
|
import httpx
|
|
|
|
|
from fastapi import Depends, FastAPI, HTTPException, Query, Request
|
|
|
|
|
@@ -54,22 +54,32 @@ engine = create_async_engine(f'sqlite+aiosqlite:///{settings.database_file}', co
|
|
|
|
|
|
|
|
|
|
async def get_session() -> AsyncSession:
|
|
|
|
|
"""SQLAlchemy session factory."""
|
|
|
|
|
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
|
|
|
|
async_session = sessionmaker(bind=engine, class_=AsyncSession, expire_on_commit=False)
|
|
|
|
|
async with async_session() as session:
|
|
|
|
|
yield session
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Shorter way of getting the DB session in an endpoint
|
|
|
|
|
SessionDep = Annotated[AsyncSession, Depends(get_session)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@asynccontextmanager
|
|
|
|
|
async def lifespan(the_app: FastAPI):
|
|
|
|
|
async def lifespan(the_app: FastAPI) -> AsyncGenerator[None, None]:
|
|
|
|
|
"""Upon start, initialise an AsyncClient and assign it to an attribute named requests_client on the app object."""
|
|
|
|
|
the_app.requests_client = httpx.AsyncClient()
|
|
|
|
|
yield
|
|
|
|
|
await the_app.requests_client.aclose()
|
|
|
|
|
async with httpx.AsyncClient() as requests_client:
|
|
|
|
|
the_app.state.requests_client = requests_client
|
|
|
|
|
yield
|
|
|
|
|
await the_app.state.requests_client.aclose()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def get_requests_client(request: Request) -> httpx.AsyncClient:
|
|
|
|
|
"""Get the httpx client from the application object."""
|
|
|
|
|
return cast(httpx.AsyncClient, request.app.state.requests_client)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Shorter way of getting the httpx client in an endpoint
|
|
|
|
|
RequestsDep = Annotated[AsyncSession, Depends(get_requests_client)]
|
|
|
|
|
|
|
|
|
|
app = FastAPI(lifespan=lifespan)
|
|
|
|
|
app.mount('/static', StaticFiles(directory=settings.static_dir), name='static')
|
|
|
|
|
app.mount('/content/favicons', StaticFiles(directory=settings.favicons_dir), name='favicons')
|
|
|
|
|
@@ -275,12 +285,24 @@ async def bookmarks_changed_since(
|
|
|
|
|
)
|
|
|
|
|
latest_created_bookmark = result.first()
|
|
|
|
|
|
|
|
|
|
latest_modification = max(latest_modified_bookmark.modified_date, latest_created_bookmark.created_date)
|
|
|
|
|
# There needs to be at least one bookmark of course
|
|
|
|
|
if latest_created_bookmark:
|
|
|
|
|
latest_created_datetime = latest_created_bookmark.created_date
|
|
|
|
|
else:
|
|
|
|
|
latest_created_datetime = datetime.min
|
|
|
|
|
|
|
|
|
|
# We only have a modified datetime when at least one has been edited
|
|
|
|
|
if latest_modified_bookmark:
|
|
|
|
|
latest_modified_datetime = latest_modified_bookmark.modified_date
|
|
|
|
|
else:
|
|
|
|
|
latest_modified_datetime = datetime.min
|
|
|
|
|
|
|
|
|
|
latest_modification = max(latest_modified_datetime, latest_created_datetime)
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
'current_time': datetime.now(UTC),
|
|
|
|
|
'latest_change': latest_modified_bookmark.modified_date,
|
|
|
|
|
'latest_created': latest_created_bookmark.created_date,
|
|
|
|
|
'latest_change': latest_modified_datetime,
|
|
|
|
|
'latest_created': latest_created_datetime,
|
|
|
|
|
'latest_modification': latest_modification,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|