Split cart into 4 microservices: relations, likes, orders, page-config→blog
Phase 1 - Relations service (internal): owns ContainerRelation, exposes get-children data + attach/detach-child actions. Retargeted events, blog, market callers from cart to relations. Phase 2 - Likes service (internal): unified Like model replaces ProductLike and PostLike with generic target_type/target_slug/target_id. Exposes is-liked, liked-slugs, liked-ids data + toggle action. Phase 3 - PageConfig → blog: moved ownership to blog with direct DB queries, removed proxy endpoints from cart. Phase 4 - Orders service (public): owns Order/OrderItem + SumUp checkout flow. Cart checkout now delegates to orders via create-order action. Webhook/return routes and reconciliation moved to orders. Phase 5 - Infrastructure: docker-compose, deploy.sh, Dockerfiles updated for all 3 new services. Added orders_url helper and factory model imports. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@ from __future__ import annotations
|
||||
|
||||
from quart import Blueprint, g, jsonify, request
|
||||
|
||||
from shared.infrastructure.data_client import DATA_HEADER, fetch_data
|
||||
from shared.infrastructure.data_client import DATA_HEADER
|
||||
from shared.contracts.dtos import dto_to_dict
|
||||
from shared.services.registry import services
|
||||
|
||||
@@ -74,31 +74,112 @@ def register() -> Blueprint:
|
||||
|
||||
_handlers["search-posts"] = _search_posts
|
||||
|
||||
# --- page-config (proxy to cart, where page_configs table lives) ---
|
||||
# --- page-config-ensure ---
|
||||
async def _page_config_ensure():
|
||||
"""Get or create a PageConfig for a container_type + container_id."""
|
||||
from sqlalchemy import select
|
||||
from shared.models.page_config import PageConfig
|
||||
|
||||
container_type = request.args.get("container_type", "page")
|
||||
container_id = request.args.get("container_id", type=int)
|
||||
if container_id is None:
|
||||
return {"error": "container_id required"}, 400
|
||||
|
||||
row = (await g.s.execute(
|
||||
select(PageConfig).where(
|
||||
PageConfig.container_type == container_type,
|
||||
PageConfig.container_id == container_id,
|
||||
)
|
||||
)).scalar_one_or_none()
|
||||
|
||||
if row is None:
|
||||
row = PageConfig(
|
||||
container_type=container_type,
|
||||
container_id=container_id,
|
||||
features={},
|
||||
)
|
||||
g.s.add(row)
|
||||
await g.s.flush()
|
||||
|
||||
return {
|
||||
"id": row.id,
|
||||
"container_type": row.container_type,
|
||||
"container_id": row.container_id,
|
||||
}
|
||||
|
||||
_handlers["page-config-ensure"] = _page_config_ensure
|
||||
|
||||
# --- page-config ---
|
||||
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)
|
||||
from sqlalchemy import select
|
||||
from shared.models.page_config import PageConfig
|
||||
|
||||
ct = request.args.get("container_type", "page")
|
||||
cid = request.args.get("container_id", type=int)
|
||||
if cid is None:
|
||||
return None
|
||||
pc = (await g.s.execute(
|
||||
select(PageConfig).where(
|
||||
PageConfig.container_type == ct,
|
||||
PageConfig.container_id == cid,
|
||||
)
|
||||
)).scalar_one_or_none()
|
||||
if not pc:
|
||||
return None
|
||||
return _page_config_dict(pc)
|
||||
|
||||
_handlers["page-config"] = _page_config
|
||||
|
||||
# --- page-config-by-id (proxy to cart) ---
|
||||
# --- page-config-by-id ---
|
||||
async def _page_config_by_id():
|
||||
return await fetch_data("cart", "page-config-by-id",
|
||||
params={"id": request.args.get("id", "")},
|
||||
required=False)
|
||||
"""Return a single PageConfig by its primary key."""
|
||||
from shared.models.page_config import PageConfig
|
||||
|
||||
pc_id = request.args.get("id", type=int)
|
||||
if pc_id is None:
|
||||
return None
|
||||
pc = await g.s.get(PageConfig, pc_id)
|
||||
if not pc:
|
||||
return None
|
||||
return _page_config_dict(pc)
|
||||
|
||||
_handlers["page-config-by-id"] = _page_config_by_id
|
||||
|
||||
# --- page-configs-batch (proxy to cart) ---
|
||||
# --- page-configs-batch ---
|
||||
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 []
|
||||
"""Return PageConfigs for multiple container_ids (comma-separated)."""
|
||||
from sqlalchemy import select
|
||||
from shared.models.page_config import PageConfig
|
||||
|
||||
ct = request.args.get("container_type", "page")
|
||||
ids_raw = request.args.get("ids", "")
|
||||
if not ids_raw:
|
||||
return []
|
||||
ids = [int(x.strip()) for x in ids_raw.split(",") if x.strip()]
|
||||
if not ids:
|
||||
return []
|
||||
result = await g.s.execute(
|
||||
select(PageConfig).where(
|
||||
PageConfig.container_type == ct,
|
||||
PageConfig.container_id.in_(ids),
|
||||
)
|
||||
)
|
||||
return [_page_config_dict(pc) for pc in result.scalars().all()]
|
||||
|
||||
_handlers["page-configs-batch"] = _page_configs_batch
|
||||
|
||||
return bp
|
||||
|
||||
|
||||
def _page_config_dict(pc) -> dict:
|
||||
"""Serialize PageConfig to a JSON-safe dict."""
|
||||
return {
|
||||
"id": pc.id,
|
||||
"container_type": pc.container_type,
|
||||
"container_id": pc.container_id,
|
||||
"features": pc.features or {},
|
||||
"sumup_merchant_code": pc.sumup_merchant_code,
|
||||
"sumup_api_key": pc.sumup_api_key,
|
||||
"sumup_checkout_prefix": pc.sumup_checkout_prefix,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user