- Add complete hosting module (models, routes, schemas, services, templates, migrations) - Add HostWizard platform to init_production seed (code=hosting, domain=hostwizard.lu) - Fix cms_002 migration down_revision to z_unique_subdomain_domain - Fix prospecting_001 migration to chain after cms_002 (remove branch label) - Add hosting/prospecting version_locations to alembic.ini - Fix admin_services delete endpoint to use proper response model - Add hostwizard.lu to deployment docs (DNS, Caddy, Cloudflare) - Add hosting and prospecting user journey docs to mkdocs nav Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
20 KiB
Hosting Module - User Journeys
Personas
| # | Persona | Role / Auth | Description |
|---|---|---|---|
| 1 | Platform Admin | admin role |
Manages the POC → live website pipeline, tracks client services, monitors renewals |
| 2 | Prospect | No auth (receives proposal link) | Views their POC website preview via a shared link |
!!! note "Admin-only module" The hosting module is primarily an admin-only module. The only non-admin page is the POC Viewer — a public preview page that shows the prospect's POC website with a HostWizard banner. Prospects do not have accounts until their proposal is accepted, at which point a Merchant account is created for them.
Lifecycle Overview
The hosting module manages the complete POC → live website pipeline:
flowchart TD
A[Prospect identified] --> B[Create Hosted Site]
B --> C[Status: DRAFT]
C --> D[Build POC website via CMS]
D --> E[Mark POC Ready]
E --> F[Status: POC_READY]
F --> G[Send Proposal to prospect]
G --> H[Status: PROPOSAL_SENT]
H --> I{Prospect accepts?}
I -->|Yes| J[Accept Proposal]
J --> K[Status: ACCEPTED]
K --> L[Merchant account created]
L --> M[Go Live with domain]
M --> N[Status: LIVE]
I -->|No| O[Cancel]
O --> P[Status: CANCELLED]
N --> Q{Issues?}
Q -->|Payment issues| R[Suspend]
R --> S[Status: SUSPENDED]
S --> T[Reactivate → LIVE]
Q -->|Client leaves| O
Status Transitions
| From | Allowed Targets |
|---|---|
draft |
poc_ready, cancelled |
poc_ready |
proposal_sent, cancelled |
proposal_sent |
accepted, cancelled |
accepted |
live, cancelled |
live |
suspended, cancelled |
suspended |
live, cancelled |
cancelled |
(terminal) |
Dev URLs (localhost:9999)
The dev server uses path-based platform routing: http://localhost:9999/platforms/hosting/...
1. Admin Pages
Login as: admin@orion.lu or samir.boulahtit@gmail.com
| Page | Dev URL |
|---|---|
| Dashboard | http://localhost:9999/platforms/hosting/admin/hosting |
| Sites List | http://localhost:9999/platforms/hosting/admin/hosting/sites |
| New Site | http://localhost:9999/platforms/hosting/admin/hosting/sites/new |
| Site Detail | http://localhost:9999/platforms/hosting/admin/hosting/sites/{site_id} |
| Client Services | http://localhost:9999/platforms/hosting/admin/hosting/clients |
2. Public Pages
| Page | Dev URL |
|---|---|
| POC Viewer | http://localhost:9999/platforms/hosting/hosting/sites/{site_id}/preview |
3. Admin API Endpoints
Sites (prefix: /platforms/hosting/api/admin/hosting/):
| Method | Endpoint | Dev URL |
|---|---|---|
| GET | list sites | http://localhost:9999/platforms/hosting/api/admin/hosting/sites |
| GET | site detail | http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{id} |
| POST | create site | http://localhost:9999/platforms/hosting/api/admin/hosting/sites |
| POST | create from prospect | http://localhost:9999/platforms/hosting/api/admin/hosting/sites/from-prospect/{prospect_id} |
| PUT | update site | http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{id} |
| DELETE | delete site | http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{id} |
Lifecycle (prefix: /platforms/hosting/api/admin/hosting/):
| Method | Endpoint | Dev URL |
|---|---|---|
| POST | mark POC ready | http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{id}/mark-poc-ready |
| POST | send proposal | http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{id}/send-proposal |
| POST | accept proposal | http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{id}/accept |
| POST | go live | http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{id}/go-live |
| POST | suspend | http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{id}/suspend |
| POST | cancel | http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{id}/cancel |
Client Services (prefix: /platforms/hosting/api/admin/hosting/):
| Method | Endpoint | Dev URL |
|---|---|---|
| GET | list services | http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{site_id}/services |
| POST | create service | http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{site_id}/services |
| PUT | update service | http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{site_id}/services/{id} |
| DELETE | delete service | http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{site_id}/services/{id} |
Stats (prefix: /platforms/hosting/api/admin/hosting/):
| Method | Endpoint | Dev URL |
|---|---|---|
| GET | dashboard stats | http://localhost:9999/platforms/hosting/api/admin/hosting/stats/dashboard |
Production URLs (hostwizard.lu)
In production, the platform uses domain-based routing.
Admin Pages & API
| Page / Endpoint | Production URL |
|---|---|
| Dashboard | https://hostwizard.lu/admin/hosting |
| Sites | https://hostwizard.lu/admin/hosting/sites |
| New Site | https://hostwizard.lu/admin/hosting/sites/new |
| Site Detail | https://hostwizard.lu/admin/hosting/sites/{id} |
| Client Services | https://hostwizard.lu/admin/hosting/clients |
| API - Sites | GET https://hostwizard.lu/api/admin/hosting/sites |
| API - Stats | GET https://hostwizard.lu/api/admin/hosting/stats/dashboard |
Public Pages
| Page | Production URL |
|---|---|
| POC Viewer | https://hostwizard.lu/hosting/sites/{site_id}/preview |
Data Model
Hosted Site
HostedSite
├── id (PK)
├── store_id (FK → stores.id, unique) # The CMS-powered website
├── prospect_id (FK → prospects.id, nullable) # Origin prospect
├── status: draft | poc_ready | proposal_sent | accepted | live | suspended | cancelled
├── business_name (str)
├── contact_name, contact_email, contact_phone
├── proposal_sent_at, proposal_accepted_at, went_live_at (datetime)
├── proposal_notes (text)
├── live_domain (str, unique)
├── internal_notes (text)
├── created_at, updated_at
└── Relationships: store, prospect, client_services
Client Service
ClientService
├── id (PK)
├── hosted_site_id (FK → hosted_sites.id, CASCADE)
├── service_type: domain | email | ssl | hosting | website_maintenance
├── name (str) # e.g., "acme.lu domain", "5 mailboxes"
├── status: pending | active | suspended | expired | cancelled
├── billing_period: monthly | annual | one_time
├── price_cents (int), currency (str, default EUR)
├── addon_product_id (FK, nullable) # Link to billing product
├── domain_name, registrar # Domain-specific
├── mailbox_count # Email-specific
├── expires_at, period_start, period_end, auto_renew
├── notes (text)
└── created_at, updated_at
User Journeys
Journey 1: Create Hosted Site from Prospect
Persona: Platform Admin Goal: Convert a qualified prospect into a hosted site with a POC website
Prerequisite: A prospect exists in the prospecting module (see Prospecting Journeys)
flowchart TD
A[View prospect in prospecting module] --> B[Click 'Create Hosted Site from Prospect']
B --> C[HostedSite created with status DRAFT]
C --> D[Store auto-created on hosting platform]
D --> E[Contact info pre-filled from prospect]
E --> F[Navigate to site detail]
F --> G[Build POC website via CMS editor]
Steps:
- Create hosted site from prospect:
- API Dev:
POST http://localhost:9999/platforms/hosting/api/admin/hosting/sites/from-prospect/{prospect_id} - API Prod:
POST https://hostwizard.lu/api/admin/hosting/sites/from-prospect/{prospect_id}
- API Dev:
- This automatically:
- Creates a Store on the hosting platform
- Creates a HostedSite record linked to the Store and Prospect
- Pre-fills business_name, contact_name, contact_email, contact_phone from prospect data
- View the new site:
- Dev:
http://localhost:9999/platforms/hosting/admin/hosting/sites/{site_id} - Prod:
https://hostwizard.lu/admin/hosting/sites/{site_id}
- Dev:
- Click the Store link to open the CMS editor and build the POC website
Journey 2: Create Hosted Site Manually
Persona: Platform Admin Goal: Create a hosted site without an existing prospect (e.g., direct referral)
flowchart TD
A[Navigate to New Site page] --> B[Fill in business details]
B --> C[Submit form]
C --> D[HostedSite + Store created]
D --> E[Navigate to site detail]
E --> F[Build POC website]
Steps:
- Navigate to New Site form:
- Dev:
http://localhost:9999/platforms/hosting/admin/hosting/sites/new - Prod:
https://hostwizard.lu/admin/hosting/sites/new
- Dev:
- Create the site:
- API Dev:
POST http://localhost:9999/platforms/hosting/api/admin/hosting/sites - API Prod:
POST https://hostwizard.lu/api/admin/hosting/sites - Body:
{ "business_name": "Boulangerie du Parc", "contact_name": "Jean Müller", "contact_email": "jean@boulangerie-parc.lu", "contact_phone": "+352 26 123 456" }
- API Dev:
- A Store is auto-created with subdomain
boulangerie-du-parcon the hosting platform
Journey 3: POC → Proposal Flow
Persona: Platform Admin Goal: Build a POC website, mark it ready, and send a proposal to the prospect
flowchart TD
A[Site is DRAFT] --> B[Build POC website via CMS]
B --> C[Mark POC Ready]
C --> D[Site is POC_READY]
D --> E[Preview the POC site]
E --> F[Send Proposal with notes]
F --> G[Site is PROPOSAL_SENT]
G --> H[Share preview link with prospect]
Steps:
- Build the POC website using the Store's CMS editor (linked from site detail page)
- When the POC is ready, mark it:
- API Dev:
POST http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{id}/mark-poc-ready - API Prod:
POST https://hostwizard.lu/api/admin/hosting/sites/{id}/mark-poc-ready
- API Dev:
- Preview the POC site (public link, no auth needed):
- Dev:
http://localhost:9999/platforms/hosting/hosting/sites/{id}/preview - Prod:
https://hostwizard.lu/hosting/sites/{id}/preview
- Dev:
- Send proposal to the prospect:
- API Dev:
POST http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{id}/send-proposal - API Prod:
POST https://hostwizard.lu/api/admin/hosting/sites/{id}/send-proposal - Body:
{ "notes": "Custom website with 5 pages, domain registration included" }
- API Dev:
- Share the preview link with the prospect via email
!!! info "POC Viewer"
The POC Viewer page renders the Store's storefront in an iframe with a teal
HostWizard banner at the top. It only works for sites with status poc_ready
or proposal_sent. Once the site goes live, the preview is disabled.
Journey 4: Accept Proposal & Create Merchant
Persona: Platform Admin Goal: When a prospect accepts, create their merchant account and subscription
flowchart TD
A[Prospect accepts proposal] --> B{Existing merchant?}
B -->|Yes| C[Link to existing merchant]
B -->|No| D[Auto-create merchant + owner account]
C --> E[Accept Proposal]
D --> E
E --> F[Site is ACCEPTED]
F --> G[Store reassigned to merchant]
G --> H[Subscription created on hosting platform]
H --> I[Prospect marked as CONVERTED]
Steps:
- Accept the proposal (auto-creates merchant if no merchant_id provided):
- API Dev:
POST http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{id}/accept - API Prod:
POST https://hostwizard.lu/api/admin/hosting/sites/{id}/accept - Body:
{}(auto-create merchant) or{ "merchant_id": 5 }(link to existing)
- API Dev:
- This automatically:
- Creates a new Merchant from contact info (name, email, phone)
- Creates a store owner account with a temporary password
- Reassigns the Store from the system merchant to the new merchant
- Creates a MerchantSubscription on the hosting platform (essential tier)
- Marks the linked prospect as CONVERTED (if prospect_id is set)
- View the updated site detail:
- Dev:
http://localhost:9999/platforms/hosting/admin/hosting/sites/{id} - Prod:
https://hostwizard.lu/admin/hosting/sites/{id}
- Dev:
!!! warning "Merchant account credentials"
When accepting without an existing merchant_id, a new merchant owner account is
created with a temporary password. The admin should communicate these credentials
to the client so they can log in and self-edit their website via the CMS.
Journey 5: Go Live with Custom Domain
Persona: Platform Admin Goal: Assign a production domain to the website and make it live
flowchart TD
A[Site is ACCEPTED] --> B[Configure DNS for client domain]
B --> C[Go Live with domain]
C --> D[Site is LIVE]
D --> E[StoreDomain created]
E --> F[Website accessible at client domain]
Steps:
- Ensure DNS is configured for the client's domain (A/AAAA records pointing to the server)
- Go live:
- API Dev:
POST http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{id}/go-live - API Prod:
POST https://hostwizard.lu/api/admin/hosting/sites/{id}/go-live - Body:
{ "domain": "boulangerie-parc.lu" }
- API Dev:
- This automatically:
- Sets
went_live_attimestamp - Creates a StoreDomain record (primary) for the domain
- Sets
live_domainon the hosted site
- Sets
- The website is now accessible at
https://boulangerie-parc.lu
Journey 6: Add Client Services
Persona: Platform Admin Goal: Track operational services (domains, email, SSL, hosting) for a client
flowchart TD
A[Open site detail] --> B[Go to Services tab]
B --> C[Add domain service]
C --> D[Add email service]
D --> E[Add SSL service]
E --> F[Add hosting service]
F --> G[Services tracked with expiry dates]
Steps:
- Navigate to site detail, Services tab:
- Dev:
http://localhost:9999/platforms/hosting/admin/hosting/sites/{site_id} - Prod:
https://hostwizard.lu/admin/hosting/sites/{site_id}
- Dev:
- Add a domain service:
- API Dev:
POST http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{site_id}/services - API Prod:
POST https://hostwizard.lu/api/admin/hosting/sites/{site_id}/services - Body:
{ "service_type": "domain", "name": "boulangerie-parc.lu domain", "domain_name": "boulangerie-parc.lu", "registrar": "Namecheap", "billing_period": "annual", "price_cents": 1500, "expires_at": "2027-03-01T00:00:00", "auto_renew": true }
- API Dev:
- Add an email service:
- Body:
{ "service_type": "email", "name": "5 mailboxes", "mailbox_count": 5, "billing_period": "monthly", "price_cents": 999 }
- Body:
- Add an SSL service:
- Body:
{ "service_type": "ssl", "name": "SSL certificate", "billing_period": "annual", "price_cents": 0, "expires_at": "2027-03-01T00:00:00" }
- Body:
- View all services for a site:
- API Dev:
GET http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{site_id}/services - API Prod:
GET https://hostwizard.lu/api/admin/hosting/sites/{site_id}/services
- API Dev:
Journey 7: Dashboard & Renewal Monitoring
Persona: Platform Admin Goal: Monitor business KPIs and upcoming service renewals
flowchart TD
A[Navigate to Dashboard] --> B[View KPIs]
B --> C[Total sites, live sites, POC sites]
C --> D[Monthly revenue]
D --> E[Active services count]
E --> F[Upcoming renewals in 30 days]
F --> G[Navigate to Client Services]
G --> H[Filter by expiring soon]
H --> I[Renew or update services]
Steps:
- Navigate to Dashboard:
- Dev:
http://localhost:9999/platforms/hosting/admin/hosting - Prod:
https://hostwizard.lu/admin/hosting
- Dev:
- View dashboard stats:
- API Dev:
GET http://localhost:9999/platforms/hosting/api/admin/hosting/stats/dashboard - API Prod:
GET https://hostwizard.lu/api/admin/hosting/stats/dashboard - Returns:
total_sites,live_sites,poc_sites,sites_by_status,active_services,monthly_revenue_cents,upcoming_renewals,services_by_type
- API Dev:
- Navigate to Client Services for detailed view:
- Dev:
http://localhost:9999/platforms/hosting/admin/hosting/clients - Prod:
https://hostwizard.lu/admin/hosting/clients
- Dev:
- Filter by type (domain, email, ssl, hosting) or status
- Toggle "Expiring Soon" to see services expiring within 30 days
Journey 8: Suspend & Reactivate
Persona: Platform Admin Goal: Handle suspension (e.g., unpaid invoices) and reactivation
Steps:
- Suspend a site:
- API Dev:
POST http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{id}/suspend - API Prod:
POST https://hostwizard.lu/api/admin/hosting/sites/{id}/suspend
- API Dev:
- Site status changes to
suspended - Once payment is resolved, reactivate by transitioning back to live:
- The
suspended → livetransition is allowed
- The
- To permanently close a site:
- API Dev:
POST http://localhost:9999/platforms/hosting/api/admin/hosting/sites/{id}/cancel - API Prod:
POST https://hostwizard.lu/api/admin/hosting/sites/{id}/cancel
- API Dev:
cancelledis a terminal state — no further transitions allowed
Journey 9: Complete Pipeline (Prospect → Live Site)
Persona: Platform Admin Goal: Walk the complete pipeline from prospect to live website
This journey combines the prospecting and hosting modules end-to-end:
flowchart TD
A[Import domain / capture lead] --> B[Enrich & score prospect]
B --> C[Create hosted site from prospect]
C --> D[Build POC website via CMS]
D --> E[Mark POC ready]
E --> F[Send proposal + share preview link]
F --> G{Prospect accepts?}
G -->|Yes| H[Accept → Merchant created]
H --> I[Add client services]
I --> J[Go live with domain]
J --> K[Website live at client domain]
K --> L[Monitor renewals & services]
G -->|No| M[Cancel or follow up later]
Steps:
- Prospecting phase (see Prospecting Journeys):
- Import domain or capture lead offline
- Run enrichment pipeline
- Score and qualify the prospect
- Create hosted site:
POST /api/admin/hosting/sites/from-prospect/{prospect_id} - Build POC: Edit the auto-created Store via CMS
- Mark POC ready:
POST /api/admin/hosting/sites/{id}/mark-poc-ready - Send proposal:
POST /api/admin/hosting/sites/{id}/send-proposal - Share preview: Send
https://hostwizard.lu/hosting/sites/{id}/previewto prospect - Accept proposal:
POST /api/admin/hosting/sites/{id}/accept - Add services:
POST /api/admin/hosting/sites/{id}/services(domain, email, SSL, hosting) - Go live:
POST /api/admin/hosting/sites/{id}/go-livewith domain - Monitor: Dashboard at
https://hostwizard.lu/admin/hosting
Recommended Test Order
- Journey 2 - Create a site manually first (simplest path, no prospect dependency)
- Journey 3 - Walk the POC → proposal flow
- Journey 4 - Accept proposal and verify merchant creation
- Journey 5 - Go live with a test domain
- Journey 6 - Add client services
- Journey 7 - Check dashboard stats
- Journey 1 - Test the prospect → hosted site conversion (requires prospecting data)
- Journey 8 - Test suspend/reactivate/cancel
- Journey 9 - Walk the complete end-to-end pipeline
!!! tip "Test Journey 2 before Journey 1" Journey 2 (manual creation) doesn't require any prospecting data and is the fastest way to verify the hosting module works. Journey 1 (from prospect) requires running the prospecting module first.