docs: architecture decision — hosting sites reuse CMS + Store + StoreDomain

Hosted sites leverage existing CMS module (ContentPage, StoreTheme,
MediaFile) instead of building a separate site rendering system. Industry
templates (restaurant, construction, auto-parts, professional-services,
generic) are JSON presets that populate CMS entities for a new Store.

POC phase uses subdomain routing (acme.hostwizard.lu), go-live adds
custom domain via StoreDomain (acme.lu). All routing handled by existing
StoreContextMiddleware + Caddy wildcards.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-31 22:42:10 +02:00
parent 3ae0b579d3
commit 91963f3b87

View File

@@ -0,0 +1,236 @@
# Architecture Decision: Hosting Sites Leverage CMS + Store + StoreDomain
## Decision
Hosted sites (POC and live) reuse the existing CMS module, Store entity, and StoreDomain routing — **no separate site builder module**.
## Rationale
The CMS module already provides everything a hosted site needs:
- **ContentPage** — store-scoped pages with three-tier inheritance (platform defaults → store overrides)
- **MediaFile** — store-scoped media library
- **StoreTheme** — per-store colors, fonts, logo, layout, custom CSS
- **Storefront rendering** — Jinja2 templates that resolve store context and render CMS content
Building a parallel system would duplicate all of this.
## How It Works
### POC Phase (DRAFT → POC_READY → PROPOSAL_SENT)
1. `HostedSite.create()` creates a Store on the `hosting` platform
2. Store inherits **default CMS pages** from the hosting platform (homepage, about, contact, etc.)
3. Admin selects an **industry template** (e.g., "restaurant", "construction") which applies:
- Pre-built page sections (hero, services, gallery, testimonials, CTA)
- Industry-appropriate theme (colors, fonts, layout)
- Placeholder content that gets replaced with prospect data
4. Admin customizes ContentPages + StoreTheme during POC phase
5. POC preview accessible at subdomain: `acme.hostwizard.lu`
### Go Live (ACCEPTED → LIVE)
1. `go_live(domain)` calls `store_domain_service.add_domain()` — already implemented
2. Custom domain `acme.lu` maps to the Store via `StoreDomain`
3. `StoreContextMiddleware` resolves `acme.lu` → Store → CMS content
4. Caddy + Cloudflare handle SSL and routing — already configured for wildcard subdomains
### Content Flow
```
Prospect scanned (prospecting module)
↓ business_name, contacts, address, tech_profile
HostedSite created → Store created on hosting platform
Industry template applied → ContentPages populated with prospect data
↓ homepage hero: "Acme Construction — Strasbourg"
↓ contact page: scraped email, phone, address
↓ theme: construction industry colors + layout
Admin refines content in CMS editor
POC preview: acme.hostwizard.lu
Client approves → go live: acme.lu
```
## Industry Template System
### What a Template Is
A template is NOT a separate rendering engine. It is a **preset bundle** that populates CMS entities for a new Store:
- **ContentPages**: pre-built page slugs with section structures (hero, services, gallery, etc.)
- **StoreTheme**: industry-appropriate colors, fonts, layout style
- **Media placeholders**: stock images appropriate to the industry
- **Section blueprints**: JSON structures for homepage sections
### Template Registry
```
app/modules/hosting/templates_library/
├── manifest.json # Registry of all templates
├── restaurant/
│ ├── meta.json # Name, description, preview image, tags
│ ├── pages/ # ContentPage seed data (JSON per page)
│ │ ├── homepage.json # Sections: hero, menu-highlights, testimonials, reservation-cta
│ │ ├── about.json # Our story, team, values
│ │ ├── menu.json # Menu categories with placeholder items
│ │ └── contact.json # Map, hours, reservation form
│ └── theme.json # Colors, fonts, layout config
├── construction/
│ ├── meta.json
│ ├── pages/
│ │ ├── homepage.json # Sections: hero, services, projects-gallery, testimonials, cta
│ │ ├── services.json # Service cards (renovation, new build, etc.)
│ │ ├── projects.json # Portfolio gallery
│ │ └── contact.json # Quote request form, address, phone
│ └── theme.json
├── auto-parts/
│ ├── meta.json
│ ├── pages/
│ │ ├── homepage.json # Sections: hero, brands, categories, promotions
│ │ ├── catalog.json # Category grid
│ │ └── contact.json # Store locations, hours
│ └── theme.json
├── professional-services/ # Lawyers, accountants, consultants
│ ├── meta.json
│ ├── pages/
│ │ ├── homepage.json # Sections: hero, expertise, team, testimonials
│ │ ├── services.json
│ │ ├── team.json
│ │ └── contact.json
│ └── theme.json
└── generic/ # Fallback for any industry
├── meta.json
├── pages/
│ ├── homepage.json
│ ├── about.json
│ └── contact.json
└── theme.json
```
### Template Meta Format
```json
{
"id": "restaurant",
"name": "Restaurant & Dining",
"description": "Elegant template for restaurants, cafés, and bars",
"preview_image": "restaurant-preview.jpg",
"tags": ["food", "dining", "hospitality"],
"languages": ["en", "fr", "de"],
"pages": ["homepage", "about", "menu", "contact"],
"default_theme": {
"theme_name": "modern",
"colors": {
"primary": "#b45309",
"secondary": "#78350f",
"accent": "#f59e0b",
"background": "#fffbeb",
"text": "#1c1917"
},
"font_family_heading": "Playfair Display",
"font_family_body": "Inter",
"layout_style": "grid",
"header_style": "transparent"
}
}
```
### Page Section Format (homepage.json example)
```json
{
"slug": "homepage",
"title": "{{business_name}}",
"template": "full",
"sections": {
"hero": {
"type": "hero",
"headline": "{{business_name}}",
"subheadline": "Quality dining in {{city}}",
"cta_text": "Reserve a Table",
"cta_link": "/contact",
"background_image": "placeholder-restaurant-hero.jpg"
},
"features": {
"type": "features_grid",
"title": "Our Specialties",
"items": [
{"icon": "utensils", "title": "Fine Dining", "description": "..."},
{"icon": "wine-glass", "title": "Wine Selection", "description": "..."},
{"icon": "cake", "title": "Pastry", "description": "..."}
]
},
"testimonials": {
"type": "testimonials",
"title": "What Our Guests Say",
"items": []
},
"cta": {
"type": "cta_banner",
"headline": "Ready to visit?",
"cta_text": "Contact Us",
"cta_link": "/contact"
}
}
}
```
`{{business_name}}`, `{{city}}`, `{{phone}}`, `{{email}}` are replaced with prospect data at POC creation time.
## What Needs to Be Built
### Phase 1 — Template Infrastructure (in hosting module)
| Component | Purpose |
|---|---|
| `hosting/templates_library/` directory | Template files (JSON) |
| `hosting/services/template_service.py` | Load manifest, list templates, validate |
| `hosting/services/poc_builder_service.py` | Apply template to Store: create ContentPages, set StoreTheme, replace placeholders with prospect data |
| `hosting/schemas/template.py` | `TemplateListResponse`, `TemplateDetailResponse` |
| API: `GET /admin/hosting/templates` | List available templates with previews |
| API: `POST /admin/hosting/poc/build` | Build POC: `{prospect_id, template_id, merchant_id}` |
### Phase 2 — Template Content
Create 5 industry templates:
1. **Restaurant** — hero, menu highlights, testimonials, reservation CTA
2. **Construction** — hero, services, project gallery, quote CTA
3. **Auto Parts** — hero, brand grid, product categories, store locator
4. **Professional Services** — hero, expertise areas, team, case studies
5. **Generic** — clean, minimal, works for any business
Each template needs:
- 3-4 page JSONs with sections
- Theme JSON (colors, fonts)
- Meta JSON (name, description, tags, preview)
- Placeholder images (can use free stock photos initially)
### Phase 3 — Storefront Rendering Enhancement
The existing storefront templates render `ContentPage.sections` as HTML. Verify:
- All section types used by templates are supported by the storefront renderer
- Homepage sections (hero, features_grid, testimonials, cta_banner) render correctly
- Theme colors/fonts apply to the storefront
If new section types are needed (e.g., `menu`, `project_gallery`, `team_grid`), add them to the storefront section renderer.
## What Already Exists (No Work Needed)
- Store creation with subdomain
- StoreDomain custom domain assignment + verification
- StoreContextMiddleware (subdomain + custom domain + path-based routing)
- CMS ContentPage three-tier hierarchy
- StoreTheme with colors, fonts, layout, custom CSS
- MediaFile upload and serving
- Storefront page rendering
- Caddy wildcard SSL for `*.hostwizard.lu`
- Cloudflare proxy + WAF for custom domains
- `go_live()` already calls `store_domain_service.add_domain()`
## Relationship to Other Proposals
- **`hosting-site-creation-fix.md`**: Must be implemented first — site creation needs merchant_id or prospect_id (no system merchant hack)
- **`security-audit-demo-poc-builder.md` Phase 4**: This document replaces Phase 4 with a more detailed architecture. The POC builder is now "apply industry template to Store CMS" rather than a vague "build better site"