diff --git a/README.md b/README.md new file mode 100644 index 0000000..e9ce240 --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ +# Shared + +Shared infrastructure, models, templates, and configuration used by all four Rose Ash microservices (blog, market, cart, events). Included as a git submodule in each app. + +## Structure + +``` +shared/ + db/ + base.py # SQLAlchemy declarative Base + session.py # Async session factory (get_session) + models/ # Shared domain models + user.py # User + magic_link.py # MagicLink (auth tokens) + domain_event.py # DomainEvent (transactional outbox) + kv.py # KeyValue (key-value store) + menu_item.py # MenuItem + ghost_membership_entities.py # GhostNewsletter, UserNewsletter + infrastructure/ + factory.py # create_base_app() — Quart app factory + cart_identity.py # current_cart_identity() (user_id or session_id) + cart_loader.py # Cart data loader for context processors + context.py # Jinja2 context processors + internal_api.py # Inter-app HTTP client (get/post via httpx) + jinja_setup.py # Jinja2 template environment setup + urls.py # URL helpers (coop_url, market_url, etc.) + user_loader.py # Load current user from session + http_utils.py # HTTP utility functions + events/ + bus.py # emit_event(), register_handler() + processor.py # EventProcessor (polls domain_events, runs handlers) + browser/app/ + csrf.py # CSRF protection + errors.py # Error handlers + middleware.py # Request/response middleware + redis_cacher.py # Tag-based Redis page caching + authz.py # Authorization helpers + filters/ # Jinja2 template filters (currency, truncate, etc.) + utils/ # HTMX helpers, UTC time, parsing + payments/sumup.py # SumUp checkout API integration + browser/templates/ # ~300 Jinja2 templates shared across all apps + config.py # YAML config loader + containers.py # ContainerType, container_filter, content_filter helpers + log_config/setup.py # Logging configuration (JSON formatter) + utils.py # host_url and other shared utilities + static/ # Shared static assets (CSS, JS, images, FontAwesome) + editor/ # Koenig (Ghost) rich text editor build + alembic/ # Database migrations (25 versions) + env.py # Imports models from all apps (with try/except guards) + versions/ # Migration files — single head: j0h8e4f6g7 +``` + +## Key Patterns + +- **App factory:** All apps call `create_base_app()` which sets up DB sessions, CSRF, error handling, event processing, logging, and the glue handler registry. +- **Event bus:** `emit_event()` writes to `domain_events` table in the caller's transaction. `EventProcessor` polls and dispatches to registered handlers. +- **Inter-app HTTP:** `internal_api.get/post("cart", "/internal/cart/summary")` for cross-app reads. URLs resolved from `app-config.yaml`. +- **Cart identity:** `current_cart_identity()` returns `{"user_id": int|None, "session_id": str|None}` from the request session. + +## Alembic Migrations + +All apps share one PostgreSQL database. Migrations are managed here and run from the blog app's entrypoint (other apps skip migrations on startup). + +```bash +# From any app directory (shared/ must be on sys.path) +alembic -c shared/alembic.ini upgrade head +``` + +Current head: `j0h8e4f6g7` (drop cross-domain FK constraints).