KOL feeds: fix dead/blocked sources, drop stale feeds (29→25)
Feed-health pass over KOL_FEEDS: - raoulpal: stale Substack (last 2024-05) → Real Vision podcast feed - dampedspring: paywalled (0 entries) → free "Damped Spring 101" Substack - unchained: Cloudflare 403 → canonical Megaphone podcast feed - lynalden: Cloudflare 202 → FeedBurner mirror - glassnode: recovered via httpx http2=True (was 403 on HTTP/1.1) - browser User-Agent + Accept headers on feed fetch - removed dead feeds with no active replacement: placeholder, dragonfly, niccarter, eugene - pin h2==4.3.0 (required by http2=True) All 25 remaining feeds verified fetching real body content; newest post per feed ≤88d. Bundles in-flight KOL-module work already in the working tree (kol_x ingest, migration 027, tests). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2,12 +2,14 @@
|
||||
API endpoints for the breakout signal monitor.
|
||||
|
||||
GET /api/signal/status — current state (enabled, recent signals)
|
||||
POST /api/signal/toggle — flip the on/off switch
|
||||
POST /api/signal/toggle — flip the on/off switch (requires X-Ingest-Key)
|
||||
GET /api/signal/history — last N signals (fired regardless of enabled state)
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi import APIRouter, Header, HTTPException
|
||||
from typing import Optional
|
||||
|
||||
from app.config import settings
|
||||
from app.services.funding_signal import (
|
||||
set_enabled,
|
||||
is_enabled,
|
||||
@@ -18,17 +20,29 @@ from app.services.funding_signal import (
|
||||
router = APIRouter(prefix="/signal", tags=["signal"])
|
||||
|
||||
|
||||
def _require_ingest_key(x_ingest_key: Optional[str]) -> None:
|
||||
"""Fail-closed operator-only guard (same pattern as signals.py)."""
|
||||
expected = settings.ingest_api_key
|
||||
if not expected:
|
||||
raise HTTPException(503, "toggle disabled (INGEST_API_KEY not configured)")
|
||||
if not x_ingest_key:
|
||||
raise HTTPException(401, "missing X-Ingest-Key header")
|
||||
if x_ingest_key != expected:
|
||||
raise HTTPException(401, "invalid X-Ingest-Key")
|
||||
|
||||
|
||||
@router.get("/status")
|
||||
async def status():
|
||||
return get_status()
|
||||
|
||||
|
||||
@router.post("/toggle")
|
||||
async def toggle(enabled: bool):
|
||||
async def toggle(enabled: bool, x_ingest_key: Optional[str] = Header(default=None)):
|
||||
"""
|
||||
Body: ?enabled=true or ?enabled=false
|
||||
Example: POST /api/signal/toggle?enabled=true
|
||||
Operator-only toggle — requires X-Ingest-Key header (same secret as signal ingest).
|
||||
Example: POST /api/signal/toggle?enabled=true -H 'X-Ingest-Key: …'
|
||||
"""
|
||||
_require_ingest_key(x_ingest_key)
|
||||
set_enabled(enabled)
|
||||
return {"enabled": is_enabled()}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user