Backend enhancements: auth, channels, restaurants, daemon improvements

- Add admin auth dependency and role checks
- Expand channel and restaurant API routes
- Improve YouTube transcript fetching
- Enhance daemon worker with better error handling and scheduling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
joungmin
2026-03-09 10:59:22 +09:00
parent d6afb62c18
commit 6c47d3c57d
9 changed files with 208 additions and 42 deletions

View File

@@ -5,10 +5,12 @@ from __future__ import annotations
import asyncio
from concurrent.futures import ThreadPoolExecutor
from fastapi import APIRouter, HTTPException
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from core import youtube
from api.deps import get_admin_user
from core import youtube, cache
_executor = ThreadPoolExecutor(max_workers=4)
@@ -23,13 +25,20 @@ class ChannelCreate(BaseModel):
@router.get("")
def list_channels():
return youtube.get_active_channels()
key = cache.make_key("channels")
cached = cache.get(key)
if cached is not None:
return cached
result = youtube.get_active_channels()
cache.set(key, result)
return result
@router.post("", status_code=201)
def create_channel(body: ChannelCreate):
def create_channel(body: ChannelCreate, _admin: dict = Depends(get_admin_user)):
try:
row_id = youtube.add_channel(body.channel_id, body.channel_name, body.title_filter)
cache.flush()
return {"id": row_id, "channel_id": body.channel_id}
except Exception as e:
if "UQ_CHANNELS_CID" in str(e).upper():
@@ -63,12 +72,15 @@ def _do_scan(channel_id: str, full: bool):
if not full and new_in_page == 0 and total_fetched > 50:
break
filtered = total_fetched - len(candidates) - len([v for v in candidates if v["video_id"] in existing_vids])
new_count = youtube.save_videos_batch(ch["id"], candidates)
return {"total_fetched": total_fetched, "new_videos": new_count}
if new_count > 0:
cache.flush()
return {"total_fetched": total_fetched, "new_videos": new_count, "filtered": filtered if title_filter else 0}
@router.post("/{channel_id}/scan")
async def scan_channel(channel_id: str, full: bool = False):
async def scan_channel(channel_id: str, full: bool = False, _admin: dict = Depends(get_admin_user)):
"""Trigger a scan for new videos from this channel (non-blocking)."""
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(_executor, _do_scan, channel_id, full)
@@ -78,7 +90,7 @@ async def scan_channel(channel_id: str, full: bool = False):
@router.delete("/{channel_id:path}")
def delete_channel(channel_id: str):
def delete_channel(channel_id: str, _admin: dict = Depends(get_admin_user)):
"""Deactivate a channel. Accepts channel_id or DB id."""
deleted = youtube.deactivate_channel(channel_id)
if not deleted:
@@ -86,4 +98,5 @@ def delete_channel(channel_id: str):
deleted = youtube.deactivate_channel_by_db_id(channel_id)
if not deleted:
raise HTTPException(404, "Channel not found")
cache.flush()
return {"ok": True}