news: clamp free + anonymous to last 6h; paid keeps 24h
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a7d657e1b4
commit
671faed707
4 changed files with 96 additions and 3 deletions
|
|
@ -19,7 +19,7 @@ from collections import defaultdict
|
|||
import httpx
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.auth import require_token
|
||||
from app.auth import require_token, maybe_current_user, CurrentUser
|
||||
from app.config import get_settings
|
||||
from app.db import get_session, utcnow
|
||||
from app.services.openrouter import (
|
||||
|
|
@ -228,11 +228,18 @@ async def news_list(
|
|||
limit: int = Query(50, ge=1, le=500),
|
||||
tags: str | None = Query(None, description="comma-separated include list"),
|
||||
exclude_tags: str | None = Query(None, description="comma-separated exclude list"),
|
||||
principal: CurrentUser | None = Depends(maybe_current_user),
|
||||
as_: str | None = Query(default=None, alias="as"),
|
||||
):
|
||||
from app.services.news_tagging import TAG_LABELS, TAG_VOCABULARY
|
||||
from app.services.access import FREE_NEWS_WINDOW_HOURS, is_paid_active
|
||||
|
||||
cutoff = utcnow() - timedelta(hours=since_hours)
|
||||
effective_hours = since_hours
|
||||
capped = not is_paid_active(principal)
|
||||
if capped:
|
||||
effective_hours = min(since_hours, FREE_NEWS_WINDOW_HOURS)
|
||||
|
||||
cutoff = utcnow() - timedelta(hours=effective_hours)
|
||||
stmt = select(Headline).where(Headline.published_at >= cutoff)
|
||||
if category:
|
||||
stmt = stmt.where(Headline.category == category)
|
||||
|
|
@ -275,7 +282,9 @@ async def news_list(
|
|||
"tag_vocabulary": TAG_VOCABULARY,
|
||||
"tag_labels": TAG_LABELS,
|
||||
"active_include": sorted(include),
|
||||
"active_exclude": sorted(exclude)},
|
||||
"active_exclude": sorted(exclude),
|
||||
"capped": capped,
|
||||
"window_hours": effective_hours},
|
||||
)
|
||||
return [HeadlineOut.model_validate(r, from_attributes=True) for r in filtered]
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ from fastapi import Depends, HTTPException, status
|
|||
from app.auth import CurrentUser, require_auth
|
||||
from app.models import User
|
||||
|
||||
# How many hours of news the free tier sees. Paid sees whatever the
|
||||
# endpoint's `since_hours` param requests (up to its own max).
|
||||
FREE_NEWS_WINDOW_HOURS = 6.0
|
||||
|
||||
|
||||
def _utcnow() -> datetime:
|
||||
return datetime.now(timezone.utc)
|
||||
|
|
|
|||
|
|
@ -33,3 +33,10 @@
|
|||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if capped %}
|
||||
<div class="news-capped-note" style="margin-top:14px; padding:10px 12px; border:1px dashed var(--border); color:var(--muted); font-size:12px; line-height:1.55;">
|
||||
Free tier — showing the last {{ window_hours|int }} hours of news.
|
||||
<a href="/pricing" style="color:var(--accent);">Upgrade</a>
|
||||
for the full 24-hour feed plus daily and weekly email digests.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue