3 Commits

Author SHA1 Message Date
giles
e1f9c964f5 Update app READMEs for monorepo
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m56s
Remove stale submodule references, port numbers, and Running sections.
Add fragment composition details. Create READMEs for federation and account.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 20:13:00 +00:00
giles
796443c06d Add monorepo README
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 20:10:23 +00:00
giles
a8d1c7a130 Add local deploy script for quick testing
Builds and restarts individual services without going through CI.
Supports explicit app names, --all, or auto-detection from git diff.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 20:09:18 +00:00
8 changed files with 329 additions and 135 deletions

138
README.md Normal file
View File

@@ -0,0 +1,138 @@
# Rose Ash
Monorepo for the Rose Ash cooperative platform — six Quart microservices sharing a common infrastructure layer, a single PostgreSQL database, and an ActivityPub federation layer.
## Services
| Service | URL | Description |
|---------|-----|-------------|
| **blog** | blog.rose-ash.com | Content management, Ghost sync, navigation, editor |
| **market** | market.rose-ash.com | Product listings, scraping, market pages |
| **cart** | cart.rose-ash.com | Shopping cart, checkout, orders, SumUp payments |
| **events** | events.rose-ash.com | Calendar, event entries, container widgets |
| **federation** | federation.rose-ash.com | OAuth2 authorization server, ActivityPub hub, social features |
| **account** | account.rose-ash.com | User dashboard, newsletters, tickets, bookings |
All services are Python 3.11 / Quart apps served by Hypercorn, deployed as a Docker Swarm stack.
## Repository structure
```
rose-ash/
├── shared/ # Common code: models, services, infrastructure, templates
│ ├── models/ # Canonical SQLAlchemy ORM models (all domains)
│ ├── services/ # Domain service implementations + registry
│ ├── contracts/ # DTOs, protocols, widget contracts
│ ├── infrastructure/ # App factory, OAuth, ActivityPub, fragments, Jinja setup
│ ├── templates/ # Shared base templates and partials
│ ├── static/ # Shared CSS, JS, images
│ ├── editor/ # Prose editor (Node build, blog only)
│ └── alembic/ # Database migrations
├── blog/ # Blog app
├── market/ # Market app
├── cart/ # Cart app
├── events/ # Events app
├── federation/ # Federation app
├── account/ # Account app
├── docker-compose.yml # Swarm stack definition
├── deploy.sh # Local build + restart script
├── .gitea/workflows/ # CI: build changed apps + deploy
├── _config/ # Runtime config (app-config.yaml)
├── schema.sql # Reference schema snapshot
└── .env # Environment variables (not committed)
```
Each app follows the same layout:
```
{app}/
├── app.py # App entry point (creates Quart app)
├── path_setup.py # Adds project root + app dir to sys.path
├── entrypoint.sh # Container entrypoint (wait for DB, run migrations, start)
├── Dockerfile # Build instructions (monorepo context)
├── bp/ # Blueprints (routes, handlers)
│ └── fragments/ # Fragment endpoints for cross-app composition
├── models/ # Re-export stubs pointing to shared/models/
├── services/ # App-specific service wiring
├── templates/ # App-specific templates (override shared/)
└── config/ # App-specific config
```
## Key architecture patterns
**Shared models** — All ORM models live in `shared/models/`. Each app's `models/` directory contains thin re-export stubs. `factory.py` imports all six apps' models at startup so SQLAlchemy relationship references resolve across domains.
**Service contracts** — Apps communicate through typed protocols (`shared/contracts/protocols.py`) and frozen dataclass DTOs (`shared/contracts/dtos.py`), wired via a singleton registry (`shared/services/registry.py`). No direct HTTP calls between apps for domain logic.
**Fragment composition** — Apps expose HTML fragments at `/internal/fragments/<type>` for cross-app UI composition. The blog fetches cart, account, navigation, and event fragments to compose its pages. Fragments are cached in Redis with short TTLs.
**OAuth SSO** — Federation is the OAuth2 authorization server. All other apps are OAuth clients with per-app first-party session cookies (Safari ITP compatible). Login/callback/logout routes are auto-registered via `shared/infrastructure/oauth.py`.
**ActivityPub** — Each app has its own AP actor (virtual projection of the same keypair). The federation app is the social hub (timeline, compose, follow, notifications). Activities are emitted to `ap_activities` table and processed by `EventProcessor`.
## Development
### Quick deploy (skip CI)
```bash
# Rebuild + restart one app
./deploy.sh blog
# Rebuild + restart multiple apps
./deploy.sh blog market
# Rebuild all
./deploy.sh --all
# Auto-detect changes from git
./deploy.sh
```
### Full stack deploy
```bash
source .env
docker stack deploy -c docker-compose.yml coop
```
### Build a single app image
```bash
docker build -f blog/Dockerfile -t registry.rose-ash.com:5000/blog:latest .
```
### Run migrations
Migrations run automatically on the **blog** service startup when `RUN_MIGRATIONS=true` is set (only blog runs migrations; all other apps skip them).
```bash
# Manual migration
docker exec -it $(docker ps -qf name=coop_blog) bash -c "cd shared && alembic upgrade head"
```
## CI/CD
A single Gitea Actions workflow (`.gitea/workflows/ci.yml`) handles all six apps:
1. Detects which files changed since the last deploy
2. If `shared/` or `docker-compose.yml` changed, rebuilds all apps
3. Otherwise rebuilds only apps with changes (or missing images)
4. Pushes images to the private registry
5. Runs `docker stack deploy` to update the swarm
### Required secrets
| Secret | Value |
|--------|-------|
| `DEPLOY_SSH_KEY` | Private SSH key for root access to the deploy host |
| `DEPLOY_HOST` | Hostname or IP of the deploy server |
## Infrastructure
- **Runtime**: Python 3.11, Quart (async Flask), Hypercorn
- **Database**: PostgreSQL 16 (shared by all apps)
- **Cache**: Redis 7 (page cache, fragment cache, sessions)
- **Orchestration**: Docker Swarm
- **Registry**: `registry.rose-ash.com:5000`
- **CI**: Gitea Actions
- **Reverse proxy**: Caddy (external, not in this repo)

36
account/README.md Normal file
View File

@@ -0,0 +1,36 @@
# Account App
User dashboard for the Rose Ash cooperative. Provides account management, newsletter preferences, and widget pages for tickets and bookings.
## Structure
```
app.py # Application factory (create_base_app + blueprints)
path_setup.py # Adds project root + app dir to sys.path
entrypoint.sh # Container entrypoint (Redis flush, start)
bp/
account/ # Dashboard, newsletters, widget pages (tickets, bookings)
auth/ # OAuth client routes + HTTP token exchange for non-coop clients
fragments/ # auth-menu fragment (sign-in button / user menu)
models/ # Re-export stubs pointing to shared/models/
services/ # register_domain_services() — wires all domains
templates/ # Account-specific templates (override shared/)
```
## Auth menu
Account serves the `auth-menu` fragment consumed by all other apps' headers. It renders either a sign-in button (anonymous) or the user's email with a dropdown (authenticated), for both desktop and mobile layouts.
## OAuth token exchange
`POST /auth/oauth/token` provides HTTP-based token exchange for non-coop OAuth clients (e.g., Artdag).
## Cross-domain communication
- `services.blog.*` — post queries for page context
- `services.calendar.*` — calendar/entry queries for bookings panel
- `services.cart.*` — cart summary + orders for tickets panel
## Fragments served
- **auth-menu** — sign-in button or user email menu (desktop + mobile)

View File

@@ -1,39 +1,26 @@
# Blog App (Coop) # Blog App (Coop)
Blog, authentication, and content management service for the Rose Ash cooperative platform. Handles Ghost CMS integration, user auth, and admin settings. Content management, Ghost CMS sync, navigation, and the prose editor for the Rose Ash cooperative platform. Runs database migrations on startup and serves as the primary content hub.
## Architecture
One of five Quart microservices sharing a single PostgreSQL database:
| App | Port | Domain |
|-----|------|--------|
| **blog (coop)** | 8000 | Auth, blog, admin, menus, snippets |
| market | 8001 | Product browsing, Suma scraping |
| cart | 8002 | Shopping cart, checkout, orders |
| events | 8003 | Calendars, bookings, tickets |
| federation | 8004 | ActivityPub, fediverse social |
## Structure ## Structure
``` ```
app.py # Application factory (create_base_app + blueprints) app.py # Application factory (create_base_app + blueprints)
path_setup.py # Adds project root + app dir to sys.path path_setup.py # Adds project root + app dir to sys.path
config/app-config.yaml # App URLs, feature flags, SumUp config entrypoint.sh # Container entrypoint (migrations, Redis flush, start)
models/ # Blog-domain models (+ re-export stubs for shared models) bp/
bp/ # Blueprints blog/ # Post listing, Ghost CMS sync, webhooks
auth/ # Magic link login, account, newsletters
blog/ # Post listing, Ghost CMS sync
post/ # Single post view and admin post/ # Single post view and admin
admin/ # Settings admin interface admin/ # Settings admin interface
menu_items/ # Navigation menu management menu_items/ # Navigation menu management
snippets/ # Reusable content snippets snippets/ # Reusable content snippets
templates/ # Jinja2 templates fragments/ # nav-tree fragment for cross-app navigation
models/ # Re-export stubs pointing to shared/models/
services/ # register_domain_services() — wires blog + calendar + market + cart services/ # register_domain_services() — wires blog + calendar + market + cart
shared/ # Submodule -> git.rose-ash.com/coop/shared.git templates/ # Blog-specific templates (override shared/)
``` ```
## Cross-Domain Communication ## Cross-domain communication
All inter-app communication uses typed service contracts (no HTTP APIs): All inter-app communication uses typed service contracts (no HTTP APIs):
@@ -41,20 +28,14 @@ All inter-app communication uses typed service contracts (no HTTP APIs):
- `services.market.*` — marketplace queries via MarketService protocol - `services.market.*` — marketplace queries via MarketService protocol
- `services.cart.*` — cart summary via CartService protocol - `services.cart.*` — cart summary via CartService protocol
- `services.federation.*` — AP publishing via FederationService protocol - `services.federation.*` — AP publishing via FederationService protocol
- `shared.services.navigation` — site navigation tree
## Domain Events ## Fragments served
- `auth/routes.py` emits `user.logged_in` via `shared.events.emit_event` - **nav-tree** — site navigation tree, fetched by all other apps
- Ghost sync emits `post.published` / `post.updated` for federation
## Running ## Fragments consumed
```bash - **cart-mini** (from cart) — cart icon + badge
export DATABASE_URL_ASYNC=postgresql+asyncpg://user:pass@localhost/coop - **auth-menu** (from account) — sign-in / user menu
export REDIS_URL=redis://localhost:6379/0 - **container-nav** (from events, market) — sidebar widgets
export SECRET_KEY=your-secret-key - **container-cards** (from events) — event cards on listing pages
alembic -c shared/alembic.ini upgrade head
hypercorn app:app --bind 0.0.0.0:8000
```

View File

@@ -1,26 +1,13 @@
# Cart App # Cart App
Shopping cart, checkout, and order management service for the Rose Ash cooperative. Shopping cart, checkout, and order management service for the Rose Ash cooperative. Integrates with SumUp for payment processing.
## Architecture
One of five Quart microservices sharing a single PostgreSQL database:
| App | Port | Domain |
|-----|------|--------|
| blog (coop) | 8000 | Auth, blog, admin, menus, snippets |
| market | 8001 | Product browsing, Suma scraping |
| **cart** | 8002 | Shopping cart, checkout, orders |
| events | 8003 | Calendars, bookings, tickets |
| federation | 8004 | ActivityPub, fediverse social |
## Structure ## Structure
``` ```
app.py # Application factory (create_base_app + blueprints) app.py # Application factory (create_base_app + blueprints)
path_setup.py # Adds project root + app dir to sys.path path_setup.py # Adds project root + app dir to sys.path
config/app-config.yaml # App URLs, SumUp config entrypoint.sh # Container entrypoint (Redis flush, start)
models/ # Cart-domain models (Order, OrderItem, PageConfig)
bp/ bp/
cart/ # Cart blueprint cart/ # Cart blueprint
global_routes.py # Add to cart, checkout, webhooks, return page global_routes.py # Add to cart, checkout, webhooks, return page
@@ -37,40 +24,33 @@ bp/
clear_cart_for_order.py # Soft-delete cart after checkout clear_cart_for_order.py # Soft-delete cart after checkout
order/ # Single order detail view order/ # Single order detail view
orders/ # Order listing view orders/ # Order listing view
fragments/ # cart-mini fragment, account-nav-item fragment
models/ # Re-export stubs (Order, OrderItem, PageConfig)
services/ # register_domain_services() — wires cart + calendar + market services/ # register_domain_services() — wires cart + calendar + market
shared/ # Submodule -> git.rose-ash.com/coop/shared.git templates/ # Cart-specific templates (override shared/)
``` ```
## Cross-Domain Communication ## Cart identity
Cart items are keyed by `user_id` (logged in) or `session_id` (anonymous). On login, anonymous cart items are adopted to the user's account.
## Cross-domain communication
- `services.calendar.*` — claim/confirm entries for orders, adopt on login - `services.calendar.*` — claim/confirm entries for orders, adopt on login
- `services.market.*` — marketplace queries for page-scoped carts - `services.market.*` — marketplace queries for page-scoped carts
- `services.blog.*` — post lookup for page context - `services.blog.*` — post lookup for page context
- `shared.services.navigation` — site navigation tree
## Domain Events ## Fragments served
- `checkout.py` emits `order.created` via `shared.events.emit_event` - **cart-mini** — cart icon + badge count
- `check_sumup_status.py` emits `order.paid` via `shared.events.emit_event` - **account-nav-item** — orders link for account nav
## Checkout Flow ## Checkout flow
```
1. User clicks "Checkout" 1. User clicks "Checkout"
2. create_order_from_cart() creates Order + OrderItems 2. `create_order_from_cart()` creates Order + OrderItems
3. services.calendar.claim_entries_for_order() marks entries as "ordered" 3. `services.calendar.claim_entries_for_order()` marks entries as "ordered"
4. emit: order.created event 4. Emits `Create` / `rose:Order` activity
5. SumUp hosted checkout created, user redirected 5. SumUp hosted checkout created, user redirected
6. SumUp webhook / return page triggers check_sumup_status() 6. SumUp webhook / return page triggers `check_sumup_status()`
7. If PAID: services.calendar.confirm_entries_for_order(), emit: order.paid 7. If PAID: `services.calendar.confirm_entries_for_order()`, emits `rose:OrderPaid`
```
## Running
```bash
export DATABASE_URL_ASYNC=postgresql+asyncpg://user:pass@localhost/coop
export REDIS_URL=redis://localhost:6379/0
export SECRET_KEY=your-secret-key
hypercorn app:app --bind 0.0.0.0:8002
```

56
deploy.sh Executable file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env bash
set -euo pipefail
REGISTRY="registry.rose-ash.com:5000"
APPS="blog market cart events federation account"
usage() {
echo "Usage: deploy.sh [app ...]"
echo " deploy.sh blog # rebuild + restart blog only"
echo " deploy.sh blog market # rebuild + restart blog and market"
echo " deploy.sh --all # rebuild + restart all apps"
echo " deploy.sh # auto-detect changed apps via git"
exit 1
}
cd "$(dirname "$0")"
# Determine which apps to build
if [ $# -eq 0 ]; then
# Auto-detect: uncommitted changes + last commit
CHANGED=$(git diff --name-only HEAD 2>/dev/null; git diff --name-only HEAD~1 HEAD 2>/dev/null || true)
BUILD=()
if echo "$CHANGED" | grep -q "^shared/"; then
BUILD=($APPS)
else
for app in $APPS; do
if echo "$CHANGED" | grep -q "^$app/"; then
BUILD+=("$app")
fi
done
fi
if [ ${#BUILD[@]} -eq 0 ]; then
echo "No app changes detected. Pass app names or --all."
exit 0
fi
elif [ "$1" = "--all" ]; then
BUILD=($APPS)
elif [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
usage
else
BUILD=("$@")
fi
echo "Building: ${BUILD[*]}"
echo ""
for app in "${BUILD[@]}"; do
echo "=== $app ==="
docker build -f "$app/Dockerfile" -t "$REGISTRY/$app:latest" .
docker push "$REGISTRY/$app:latest"
docker service update --force "coop_$app" 2>/dev/null \
|| echo " (service coop_$app not running — will start on next stack deploy)"
echo ""
done
echo "Done."

View File

@@ -2,28 +2,13 @@
Calendar and event booking service for the Rose Ash cooperative platform. Manages calendars, time slots, calendar entries (bookings), tickets, and ticket types. Calendar and event booking service for the Rose Ash cooperative platform. Manages calendars, time slots, calendar entries (bookings), tickets, and ticket types.
## Architecture
One of five Quart microservices sharing a single PostgreSQL database:
| App | Port | Domain |
|-----|------|--------|
| blog (coop) | 8000 | Auth, blog, admin, menus, snippets |
| market | 8001 | Product browsing, Suma scraping |
| cart | 8002 | Shopping cart, checkout, orders |
| **events** | 8003 | Calendars, bookings, tickets |
| federation | 8004 | ActivityPub, fediverse social |
## Structure ## Structure
``` ```
app.py # Application factory (create_base_app + blueprints) app.py # Application factory (create_base_app + blueprints)
path_setup.py # Adds project root + app dir to sys.path path_setup.py # Adds project root + app dir to sys.path
config/app-config.yaml # App URLs, feature flags entrypoint.sh # Container entrypoint (Redis flush, start)
models/ # Events-domain models bp/
calendars.py # Calendar, CalendarEntry, CalendarSlot,
# TicketType, Ticket, CalendarEntryPost
bp/ # Blueprints
calendars/ # Calendar listing calendars/ # Calendar listing
calendar/ # Single calendar view and admin calendar/ # Single calendar view and admin
calendar_entries/ # Calendar entries listing calendar_entries/ # Calendar entries listing
@@ -37,18 +22,20 @@ bp/ # Blueprints
ticket_admin/ # Ticket administration ticket_admin/ # Ticket administration
markets/ # Page-scoped marketplace views markets/ # Page-scoped marketplace views
payments/ # Payment-related views payments/ # Payment-related views
fragments/ # container-nav, container-cards fragments
models/ # Re-export stubs (Calendar, CalendarEntry, Ticket, etc.)
services/ # register_domain_services() — wires calendar + market + cart services/ # register_domain_services() — wires calendar + market + cart
shared/ # Submodule -> git.rose-ash.com/coop/shared.git templates/ # Events-specific templates (override shared/)
``` ```
## Models ## Models
All events-domain models live in `models/calendars.py`: All events-domain models live in `shared/models/`:
| Model | Description | | Model | Description |
|-------|-------------| |-------|-------------|
| **Calendar** | Container for entries, scoped to a page via `container_type + container_id` | | **Calendar** | Container for entries, scoped to a page via `container_type + container_id` |
| **CalendarEntry** | A bookable event/time slot. Has `state` (pending/ordered/provisional), `cost`, ownership (`user_id`/`session_id`), and `order_id` (plain integer, no FK) | | **CalendarEntry** | A bookable event/time slot with state (pending/ordered/provisional) and cost |
| **CalendarSlot** | Recurring time bands (day-of-week + time range) within a calendar | | **CalendarSlot** | Recurring time bands (day-of-week + time range) within a calendar |
| **TicketType** | Named ticket categories with price and count | | **TicketType** | Named ticket categories with price and count |
| **Ticket** | Individual ticket with unique code, state, and `order_id` (plain integer, no FK) | | **Ticket** | Individual ticket with unique code, state, and `order_id` (plain integer, no FK) |
@@ -56,23 +43,13 @@ All events-domain models live in `models/calendars.py`:
`order_id` on CalendarEntry and Ticket is a plain integer column — no FK constraint to the orders table. The cart app writes these values via service calls, not directly. `order_id` on CalendarEntry and Ticket is a plain integer column — no FK constraint to the orders table. The cart app writes these values via service calls, not directly.
## Cross-Domain Communication ## Cross-domain communication
- `services.market.*` — marketplace queries for page views - `services.market.*` — marketplace queries for page views
- `services.cart.*` — cart summary for context processor - `services.cart.*` — cart summary for context processor
- `services.federation.*` — AP publishing for new entries - `services.federation.*` — AP publishing for new entries
- `shared.services.navigation` — site navigation tree
## Migrations ## Fragments served
This app does **not** run Alembic migrations on startup. Migrations are managed in the `shared/` submodule and run from the blog app's entrypoint. - **container-nav** — calendar entries + links for blog sidebar
- **container-cards** — event cards for blog listing pages
## Running
```bash
export DATABASE_URL_ASYNC=postgresql+asyncpg://user:pass@localhost/coop
export REDIS_URL=redis://localhost:6379/0
export SECRET_KEY=your-secret-key
hypercorn app:app --bind 0.0.0.0:8003
```

44
federation/README.md Normal file
View File

@@ -0,0 +1,44 @@
# Federation App
OAuth2 authorization server and ActivityPub social hub for the Rose Ash cooperative. Handles user authentication, fediverse federation, and social features (timeline, compose, follow, notifications).
## Structure
```
app.py # Application factory (create_base_app + blueprints)
path_setup.py # Adds project root + app dir to sys.path
entrypoint.sh # Container entrypoint (optional migrations, Redis flush, start)
bp/
auth/ # Login, magic link, logout, OAuth2 authorize endpoint
identity/ # Actor profile management
social/ # Timeline, compose, search, follow/unfollow,
# like/boost, notifications, following/followers lists
fragments/ # Fragment endpoints
models/ # Re-export stubs pointing to shared/models/
services/ # register_domain_services() — wires all domains
templates/ # Federation-specific templates (override shared/)
```
## OAuth2 SSO
Federation is the authorization server for all Rose Ash apps:
- `/oauth/authorize` — authorization endpoint (PKCE supported)
- Magic link login (passwordless email)
- All client apps auto-get `/auth/login`, `/auth/callback`, `/auth/logout` via `shared/infrastructure/oauth.py`
- Per-app first-party session cookies (Safari ITP compatible)
## ActivityPub
- Per-app actors: each app has its own AP actor (virtual projection of the same keypair)
- Actor endpoints: webfinger, actor profile, inbox, outbox, followers
- Social features: timeline, compose, search, follow/unfollow, like/boost, notifications
- Activities emitted to `ap_activities` table, processed by `EventProcessor`
- HTTP signatures + IPFS content addressing
## Cross-domain communication
- `services.blog.*` — post queries for content federation
- `services.calendar.*` — event queries
- `services.market.*` — product queries
- `services.cart.*` — cart summary for context processor

View File

@@ -2,41 +2,33 @@
Product browsing and marketplace service for the Rose Ash cooperative. Displays products scraped from Suma Wholesale. Product browsing and marketplace service for the Rose Ash cooperative. Displays products scraped from Suma Wholesale.
## Architecture
One of five Quart microservices sharing a single PostgreSQL database:
| App | Port | Domain |
|-----|------|--------|
| blog (coop) | 8000 | Auth, blog, admin, menus, snippets |
| **market** | 8001 | Product browsing, Suma scraping |
| cart | 8002 | Shopping cart, checkout, orders |
| events | 8003 | Calendars, bookings, tickets |
| federation | 8004 | ActivityPub, fediverse social |
## Structure ## Structure
``` ```
app.py # Application factory (create_base_app + blueprints) app.py # Application factory (create_base_app + blueprints)
path_setup.py # Adds project root + app dir to sys.path path_setup.py # Adds project root + app dir to sys.path
config/app-config.yaml # App URLs, feature flags entrypoint.sh # Container entrypoint (Redis flush, start)
models/ # Market-domain models (+ re-export stubs) bp/
bp/ # Blueprints
market/ # Market root, navigation, category listing market/ # Market root, navigation, category listing
browse/ # Product browsing with filters and infinite scroll browse/ # Product browsing with filters and infinite scroll
product/ # Product detail pages product/ # Product detail pages
cart/ # Page-scoped cart views cart/ # Page-scoped cart views
api/ # Product sync API (used by scraper) api/ # Product sync API (used by scraper)
fragments/ # container-nav fragment (market links)
scrape/ # Suma Wholesale scraper scrape/ # Suma Wholesale scraper
models/ # Re-export stubs pointing to shared/models/
services/ # register_domain_services() — wires market + cart services/ # register_domain_services() — wires market + cart
shared/ # Submodule -> git.rose-ash.com/coop/shared.git templates/ # Market-specific templates (override shared/)
``` ```
## Cross-Domain Communication ## Cross-domain communication
- `services.cart.*` — cart summary via CartService protocol - `services.cart.*` — cart summary via CartService protocol
- `services.federation.*` — AP publishing via FederationService protocol - `services.federation.*` — AP publishing via FederationService protocol
- `shared.services.navigation` — site navigation tree
## Fragments served
- **container-nav** — market navigation links for blog sidebar
## Scraping ## Scraping
@@ -44,13 +36,3 @@ shared/ # Submodule -> git.rose-ash.com/coop/shared.git
bash scrape.sh # Full Suma Wholesale catalogue bash scrape.sh # Full Suma Wholesale catalogue
bash scrape-test.sh # Limited test scrape bash scrape-test.sh # Limited test scrape
``` ```
## Running
```bash
export DATABASE_URL_ASYNC=postgresql+asyncpg://user:pass@localhost/coop
export REDIS_URL=redis://localhost:6379/0
export SECRET_KEY=your-secret-key
hypercorn app:app --bind 0.0.0.0:8001
```