page_configs table lives in db_cart but blog was querying it directly, causing UndefinedTableError. Move all PageConfig read/write endpoints to cart service and have blog proxy via fetch_data/call_action. Also fix OAuth callback to use code_hash lookup (codes are now stored hashed) and pass grant_token in redirect URL to prevent auth loops. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
105 lines
3.6 KiB
Python
105 lines
3.6 KiB
Python
"""Blog app data endpoints.
|
|
|
|
Exposes read-only JSON queries at ``/internal/data/<query_name>`` for
|
|
cross-app callers via the internal data client.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from quart import Blueprint, g, jsonify, request
|
|
|
|
from shared.infrastructure.data_client import DATA_HEADER, fetch_data
|
|
from shared.contracts.dtos import dto_to_dict
|
|
from shared.services.registry import services
|
|
|
|
|
|
def register() -> Blueprint:
|
|
bp = Blueprint("data", __name__, url_prefix="/internal/data")
|
|
|
|
@bp.before_request
|
|
async def _require_data_header():
|
|
if not request.headers.get(DATA_HEADER):
|
|
return jsonify({"error": "forbidden"}), 403
|
|
from shared.infrastructure.internal_auth import validate_internal_request
|
|
if not validate_internal_request():
|
|
return jsonify({"error": "forbidden"}), 403
|
|
|
|
_handlers: dict[str, object] = {}
|
|
|
|
@bp.get("/<query_name>")
|
|
async def handle_query(query_name: str):
|
|
handler = _handlers.get(query_name)
|
|
if handler is None:
|
|
return jsonify({"error": "unknown query"}), 404
|
|
result = await handler()
|
|
return jsonify(result)
|
|
|
|
# --- post-by-slug ---
|
|
async def _post_by_slug():
|
|
slug = request.args.get("slug", "")
|
|
post = await services.blog.get_post_by_slug(g.s, slug)
|
|
if not post:
|
|
return None
|
|
return dto_to_dict(post)
|
|
|
|
_handlers["post-by-slug"] = _post_by_slug
|
|
|
|
# --- post-by-id ---
|
|
async def _post_by_id():
|
|
post_id = int(request.args.get("id", 0))
|
|
post = await services.blog.get_post_by_id(g.s, post_id)
|
|
if not post:
|
|
return None
|
|
return dto_to_dict(post)
|
|
|
|
_handlers["post-by-id"] = _post_by_id
|
|
|
|
# --- posts-by-ids ---
|
|
async def _posts_by_ids():
|
|
ids_raw = request.args.get("ids", "")
|
|
if not ids_raw:
|
|
return []
|
|
ids = [int(x.strip()) for x in ids_raw.split(",") if x.strip()]
|
|
posts = await services.blog.get_posts_by_ids(g.s, ids)
|
|
return [dto_to_dict(p) for p in posts]
|
|
|
|
_handlers["posts-by-ids"] = _posts_by_ids
|
|
|
|
# --- search-posts ---
|
|
async def _search_posts():
|
|
query = request.args.get("query", "")
|
|
page = int(request.args.get("page", 1))
|
|
per_page = int(request.args.get("per_page", 10))
|
|
posts, total = await services.blog.search_posts(g.s, query, page, per_page)
|
|
return {"posts": [dto_to_dict(p) for p in posts], "total": total}
|
|
|
|
_handlers["search-posts"] = _search_posts
|
|
|
|
# --- page-config (proxy to cart, where page_configs table lives) ---
|
|
async def _page_config():
|
|
"""Return a single PageConfig by container_type + container_id."""
|
|
return await fetch_data("cart", "page-config",
|
|
params={"container_type": request.args.get("container_type", "page"),
|
|
"container_id": request.args.get("container_id", "")},
|
|
required=False)
|
|
|
|
_handlers["page-config"] = _page_config
|
|
|
|
# --- page-config-by-id (proxy to cart) ---
|
|
async def _page_config_by_id():
|
|
return await fetch_data("cart", "page-config-by-id",
|
|
params={"id": request.args.get("id", "")},
|
|
required=False)
|
|
|
|
_handlers["page-config-by-id"] = _page_config_by_id
|
|
|
|
# --- page-configs-batch (proxy to cart) ---
|
|
async def _page_configs_batch():
|
|
return await fetch_data("cart", "page-configs-batch",
|
|
params={"container_type": request.args.get("container_type", "page"),
|
|
"ids": request.args.get("ids", "")},
|
|
required=False) or []
|
|
|
|
_handlers["page-configs-batch"] = _page_configs_batch
|
|
|
|
return bp
|