This repository has been archived on 2026-02-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
shared/contracts/dtos.py
giles 8850a0106a Add federation/ActivityPub models, contracts, and services
Phase 0+1 of ActivityPub integration:
- 6 ORM models (ActorProfile, APActivity, APFollower, APInboxItem, APAnchor, IPFSPin)
- FederationService protocol + SqlFederationService implementation + stub
- 4 DTOs (ActorProfileDTO, APActivityDTO, APFollowerDTO, APAnchorDTO)
- Registry slot for federation service
- Alembic migration for federation tables
- IPFS async client (httpx-based)
- HTTP Signatures (RSA-2048 sign/verify)
- login_url() now uses AUTH_APP env var for flexible auth routing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 15:10:08 +00:00

190 lines
5.0 KiB
Python

"""Frozen dataclasses for cross-domain data transfer.
These are the *only* shapes that cross domain boundaries. Consumers never
see ORM model instances from another domain — only these DTOs.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime
from decimal import Decimal
# ---------------------------------------------------------------------------
# Blog domain
# ---------------------------------------------------------------------------
@dataclass(frozen=True, slots=True)
class PostDTO:
id: int
slug: str
title: str
status: str
visibility: str
is_page: bool = False
feature_image: str | None = None
html: str | None = None
excerpt: str | None = None
custom_excerpt: str | None = None
published_at: datetime | None = None
# ---------------------------------------------------------------------------
# Calendar / Events domain
# ---------------------------------------------------------------------------
@dataclass(frozen=True, slots=True)
class CalendarDTO:
id: int
container_type: str
container_id: int
name: str
slug: str
description: str | None = None
@dataclass(frozen=True, slots=True)
class TicketDTO:
id: int
code: str
state: str
entry_name: str
entry_start_at: datetime
entry_end_at: datetime | None = None
ticket_type_name: str | None = None
calendar_name: str | None = None
created_at: datetime | None = None
checked_in_at: datetime | None = None
entry_id: int | None = None
ticket_type_id: int | None = None
price: Decimal | None = None
order_id: int | None = None
calendar_container_id: int | None = None
@dataclass(frozen=True, slots=True)
class CalendarEntryDTO:
id: int
calendar_id: int
name: str
start_at: datetime
state: str
cost: Decimal
end_at: datetime | None = None
user_id: int | None = None
session_id: str | None = None
order_id: int | None = None
slot_id: int | None = None
ticket_price: Decimal | None = None
ticket_count: int | None = None
calendar_name: str | None = None
calendar_slug: str | None = None
calendar_container_id: int | None = None
calendar_container_type: str | None = None
# ---------------------------------------------------------------------------
# Market domain
# ---------------------------------------------------------------------------
@dataclass(frozen=True, slots=True)
class MarketPlaceDTO:
id: int
container_type: str
container_id: int
name: str
slug: str
description: str | None = None
@dataclass(frozen=True, slots=True)
class ProductDTO:
id: int
slug: str
title: str | None = None
image: str | None = None
description_short: str | None = None
rrp: Decimal | None = None
regular_price: Decimal | None = None
special_price: Decimal | None = None
# ---------------------------------------------------------------------------
# Cart domain
# ---------------------------------------------------------------------------
@dataclass(frozen=True, slots=True)
class CartItemDTO:
id: int
product_id: int
quantity: int
product_title: str | None = None
product_slug: str | None = None
product_image: str | None = None
unit_price: Decimal | None = None
market_place_id: int | None = None
@dataclass(frozen=True, slots=True)
class CartSummaryDTO:
count: int = 0
total: Decimal = Decimal("0")
calendar_count: int = 0
calendar_total: Decimal = Decimal("0")
items: list[CartItemDTO] = field(default_factory=list)
ticket_count: int = 0
ticket_total: Decimal = Decimal("0")
# ---------------------------------------------------------------------------
# Federation / ActivityPub domain
# ---------------------------------------------------------------------------
@dataclass(frozen=True, slots=True)
class ActorProfileDTO:
id: int
user_id: int
preferred_username: str
public_key_pem: str
display_name: str | None = None
summary: str | None = None
inbox_url: str | None = None
outbox_url: str | None = None
created_at: datetime | None = None
@dataclass(frozen=True, slots=True)
class APActivityDTO:
id: int
activity_id: str
activity_type: str
actor_profile_id: int
object_type: str | None = None
object_data: dict | None = None
published: datetime | None = None
is_local: bool = True
source_type: str | None = None
source_id: int | None = None
ipfs_cid: str | None = None
@dataclass(frozen=True, slots=True)
class APFollowerDTO:
id: int
actor_profile_id: int
follower_acct: str
follower_inbox: str
follower_actor_url: str
created_at: datetime | None = None
@dataclass(frozen=True, slots=True)
class APAnchorDTO:
id: int
merkle_root: str
activity_count: int = 0
tree_ipfs_cid: str | None = None
ots_proof_cid: str | None = None
confirmed_at: datetime | None = None
bitcoin_txid: str | None = None