Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/admin-api/src/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
# Each service owns its cached clients and dependency identity.
get_session = session_dependency(session_factory)
get_redis = redis_dependency(get_settings)
DB = Annotated[Session, Depends(get_session, scope="function")]
database_session = Depends(get_session, scope="function")
DB = Annotated[Session, database_session]
79 changes: 26 additions & 53 deletions apps/admin-api/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,17 @@

import asyncio
import logging
from contextlib import asynccontextmanager
from contextlib import AsyncExitStack, asynccontextmanager

from devfeed_core.client_lifecycle import close_shared_clients
from devfeed_core.config import get_settings as core_settings
from devfeed_core.db import database_revision
from devfeed_core.logging import configure_logging
from devfeed_core.telemetry import start_runtime, stop_runtime
from devfeed_core.version import SCHEMA_REVISION, __version__
from devfeed_http.admission import AdmissionMiddleware
from devfeed_http.errors import register_error_handlers
from devfeed_http.health import readiness_response
from devfeed_http.logging import RequestLoggingMiddleware
from devfeed_http.schemas import ERROR_RESPONSES, HealthResponse, UnhealthyResponse
from devfeed_http.schemas import ERROR_RESPONSES
from devfeed_http.service import HTTPService
from devfeed_http.telemetry import fastapi_telemetry
from fastapi import FastAPI
from starlette.concurrency import run_in_threadpool

from devfeed_admin_api import (
ai_connection,
Expand All @@ -44,31 +39,31 @@
)
from devfeed_admin_api.codex_connection import CodexConnection
from devfeed_admin_api.config import get_settings
from devfeed_admin_api.dependencies import DB, get_redis
from devfeed_admin_api.dependencies import database_session, get_redis
from devfeed_admin_api.reporting import close_reporting

logger = logging.getLogger(__name__)


def close_clients():
close_reporting()
close_shared_clients(get_redis)
try:
close_reporting()
finally:
close_shared_clients(get_redis)


@asynccontextmanager
async def lifespan(app):
telemetry = start_runtime("admin-api")
logger.info("admin_api_started")
app.state.codex.start()
try:
async def admin_resources(app):
async with AsyncExitStack() as stack:
stack.push_async_callback(app.state.codex.close)
app.state.codex.start()
stack.push_async_callback(overview_panels.close_panel_tasks, app)
stack.push_async_callback(overview.close_snapshot_tasks, app)
yield
finally:
await overview.close_snapshot_tasks(app)
await overview_panels.close_panel_tasks(app)
await app.state.codex.close()
await run_in_threadpool(close_clients)
await run_in_threadpool(stop_runtime, telemetry)
logger.info("admin_api_stopped")


service = HTTPService("admin-api", logger, lambda: close_clients(), admin_resources)
lifespan = service.lifespan


def create_app() -> FastAPI:
Expand All @@ -91,37 +86,15 @@ def create_app() -> FastAPI:
app.state.overview_tasks = {}
app.state.overview_retry_at = {}
# No cross-origin cookie access: the Next.js admin service proxies same-origin requests.
app.add_middleware(
AdmissionMiddleware,
requests=settings.api_max_concurrent_requests,
streams=settings.api_max_concurrent_streams,
)
app.add_middleware(
RequestLoggingMiddleware,
service="admin-api",
logger=logger,
response_headers=((b"cache-control", b"no-store"), (b"referrer-policy", b"no-referrer")),
)

register_error_handlers(app, logger, admin=True)

@app.get("/health/live", tags=["health"], response_model=HealthResponse)
async def live():
return {"status": "ok"}

@app.get(
"/health/ready",
tags=["health"],
response_model=HealthResponse,
responses={503: {"model": UnhealthyResponse, "description": "Not ready"}},
service.configure(app, settings, private=True)

service.register_health(
app,
session_dependency=database_session,
get_redis=lambda: get_redis(),
revision_reader=lambda session: database_revision(session),
schema_revision=SCHEMA_REVISION,
)
def ready(session: DB):
return readiness_response(
session,
get_redis(),
revision_reader=database_revision,
schema_revision=SCHEMA_REVISION,
)

for router in (
auth.router,
Expand Down
3 changes: 2 additions & 1 deletion apps/aggregator/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ description = "Scheduled RSS and Atom ingestion workers"
requires-python = ">=3.12"
dependencies = [
"devfeed-core",
"devfeed-notifications",
"rq>=2.8,<3",
]

[project.optional-dependencies]
notifications = ["devfeed-notifications"]
article-enrichment = ["lingua-language-detector>=2.2,<3", "trafilatura>=2.2,<3"]
images = ["boto3>=1.40,<2", "pillow>=11.3,<13", "cairosvg>=2.8,<3"]
ai = ["websockets>=15,<18"]
all-workers = [
"devfeed-notifications",
"lingua-language-detector>=2.2,<3",
"trafilatura>=2.2,<3",
"websockets>=15,<18",
Expand Down
Loading