2026-03-02 01:48:51 +01:00
|
|
|
"""News articles router -- paginated, filterable by category."""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
|
|
|
|
|
|
from fastapi import APIRouter, Query
|
|
|
|
|
|
|
|
|
|
from server.cache import cache
|
feat: add Admin Panel with JWT auth, DB settings, and integration management
Complete admin backend with login, where all integrations (weather, news,
Home Assistant, Vikunja, Unraid, MQTT) can be configured via web UI instead
of ENV variables. Two-layer config: ENV seeds DB on first start, then DB
is source of truth. Auto-migration system on startup.
Backend: db.py shared pool, auth.py JWT, settings_service CRUD, seed_service,
admin router (protected), test_connections per integration, config.py rewrite.
Frontend: react-router v6, login page, admin layout with sidebar, 8 settings
pages (General, Weather, News, HA, Vikunja, Unraid, MQTT, ChangePassword),
shared IntegrationForm + TestButton components.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 10:37:30 +01:00
|
|
|
from server.config import get_settings
|
2026-03-02 01:48:51 +01:00
|
|
|
from server.services.news_service import get_news, get_news_count
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
router = APIRouter(prefix="/api", tags=["news"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _cache_key(limit: int, offset: int, category: Optional[str]) -> str:
|
|
|
|
|
return f"news:{limit}:{offset}:{category}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/news")
|
|
|
|
|
async def get_news_articles(
|
|
|
|
|
limit: int = Query(default=20, le=50, ge=1),
|
|
|
|
|
offset: int = Query(default=0, ge=0),
|
|
|
|
|
category: Optional[str] = Query(default=None),
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
"""Return a paginated list of news articles.
|
|
|
|
|
|
|
|
|
|
Response shape::
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
"articles": [ ... ],
|
|
|
|
|
"total": int,
|
|
|
|
|
"limit": int,
|
|
|
|
|
"offset": int,
|
|
|
|
|
}
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
key = _cache_key(limit, offset, category)
|
|
|
|
|
|
|
|
|
|
# --- cache hit? -----------------------------------------------------------
|
|
|
|
|
cached = await cache.get(key)
|
|
|
|
|
if cached is not None:
|
2026-03-02 17:45:23 +01:00
|
|
|
logger.debug("[NEWS] Cache hit (key=%s)", key)
|
2026-03-02 01:48:51 +01:00
|
|
|
return cached
|
|
|
|
|
|
|
|
|
|
# --- cache miss -----------------------------------------------------------
|
|
|
|
|
articles: List[Dict[str, Any]] = []
|
|
|
|
|
total: int = 0
|
|
|
|
|
|
|
|
|
|
try:
|
feat: add Admin Panel with JWT auth, DB settings, and integration management
Complete admin backend with login, where all integrations (weather, news,
Home Assistant, Vikunja, Unraid, MQTT) can be configured via web UI instead
of ENV variables. Two-layer config: ENV seeds DB on first start, then DB
is source of truth. Auto-migration system on startup.
Backend: db.py shared pool, auth.py JWT, settings_service CRUD, seed_service,
admin router (protected), test_connections per integration, config.py rewrite.
Frontend: react-router v6, login page, admin layout with sidebar, 8 settings
pages (General, Weather, News, HA, Vikunja, Unraid, MQTT, ChangePassword),
shared IntegrationForm + TestButton components.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 10:37:30 +01:00
|
|
|
articles = await get_news(limit=limit, offset=offset, category=category, max_age_hours=get_settings().news_max_age_hours)
|
2026-03-02 17:45:23 +01:00
|
|
|
logger.info("[NEWS] Fetched %d articles (limit=%d, offset=%d, category=%s)",
|
|
|
|
|
len(articles), limit, offset, category)
|
2026-03-02 01:48:51 +01:00
|
|
|
except Exception as exc:
|
2026-03-02 17:45:23 +01:00
|
|
|
logger.exception("[NEWS] Failed to fetch articles")
|
2026-03-02 01:48:51 +01:00
|
|
|
return {
|
|
|
|
|
"articles": [],
|
|
|
|
|
"total": 0,
|
|
|
|
|
"limit": limit,
|
|
|
|
|
"offset": offset,
|
|
|
|
|
"error": True,
|
|
|
|
|
"message": str(exc),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try:
|
feat: add Admin Panel with JWT auth, DB settings, and integration management
Complete admin backend with login, where all integrations (weather, news,
Home Assistant, Vikunja, Unraid, MQTT) can be configured via web UI instead
of ENV variables. Two-layer config: ENV seeds DB on first start, then DB
is source of truth. Auto-migration system on startup.
Backend: db.py shared pool, auth.py JWT, settings_service CRUD, seed_service,
admin router (protected), test_connections per integration, config.py rewrite.
Frontend: react-router v6, login page, admin layout with sidebar, 8 settings
pages (General, Weather, News, HA, Vikunja, Unraid, MQTT, ChangePassword),
shared IntegrationForm + TestButton components.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 10:37:30 +01:00
|
|
|
total = await get_news_count(max_age_hours=get_settings().news_max_age_hours, category=category)
|
2026-03-02 01:48:51 +01:00
|
|
|
except Exception as exc:
|
|
|
|
|
logger.exception("Failed to fetch news count")
|
|
|
|
|
# We still have articles -- return them with total = len(articles)
|
|
|
|
|
total = len(articles)
|
|
|
|
|
|
|
|
|
|
payload: Dict[str, Any] = {
|
|
|
|
|
"articles": articles,
|
|
|
|
|
"total": total,
|
|
|
|
|
"limit": limit,
|
|
|
|
|
"offset": offset,
|
|
|
|
|
}
|
|
|
|
|
|
feat: add Admin Panel with JWT auth, DB settings, and integration management
Complete admin backend with login, where all integrations (weather, news,
Home Assistant, Vikunja, Unraid, MQTT) can be configured via web UI instead
of ENV variables. Two-layer config: ENV seeds DB on first start, then DB
is source of truth. Auto-migration system on startup.
Backend: db.py shared pool, auth.py JWT, settings_service CRUD, seed_service,
admin router (protected), test_connections per integration, config.py rewrite.
Frontend: react-router v6, login page, admin layout with sidebar, 8 settings
pages (General, Weather, News, HA, Vikunja, Unraid, MQTT, ChangePassword),
shared IntegrationForm + TestButton components.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 10:37:30 +01:00
|
|
|
await cache.set(key, payload, get_settings().news_cache_ttl)
|
2026-03-02 01:48:51 +01:00
|
|
|
return payload
|