- Fix IPv6 host parsing with _strip_port() utility - Remove dangerous StorePlatform→Store.subdomain silent fallback - Close storefront gate bypass when frontend_type is None - Add custom subdomain management UI and API for stores - Add domain health diagnostic tool - Convert db.add() in loops to db.add_all() (24 PERF-006 fixes) - Add tests for all new functionality (18 subdomain service tests) - Add .github templates for validator compliance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.7 KiB
Domain Health Diagnostic Tool
The Domain Health tool simulates the middleware resolution pipeline for every configured store access method, verifying that each domain, subdomain, and path-based route resolves to the expected store.
Overview
| Aspect | Detail |
|---|---|
| Location | Admin > Diagnostics > Resolution > Domain Health |
| URL | /admin/platform-debug (select "Domain Health" in sidebar) |
| API Endpoint | GET /api/v1/admin/debug/domain-health |
| Access | Super admin only |
What It Checks
The tool runs four categories of checks against the live database:
1. Custom Subdomains (StorePlatform)
Checks every active StorePlatform entry that has a custom_subdomain set.
| Example | What It Tests |
|---|---|
acme-rewards.rewardflow.lu |
Does detection_method=subdomain with custom_subdomain + platform context resolve to the expected store? |
This is the most critical check because the fail-closed routing policy means a custom subdomain that doesn't resolve will return a 404 rather than falling back to a global lookup.
2. Default Subdomains per Platform
Checks every active store's default subdomain on each platform it belongs to.
| Example | What It Tests |
|---|---|
acme.omsflow.lu |
Does detection_method=subdomain with Store.subdomain + platform context resolve to the expected store? |
The middleware first tries StorePlatform.custom_subdomain, then falls back to Store.subdomain — but only if the store has an active StorePlatform membership on the detected platform. A store with no membership on that platform will be blocked (fail-closed, prevents cross-tenant leaks).
Entries where custom_subdomain already equals the store's subdomain are de-duplicated (covered by check #1).
3. Custom Domains (StoreDomain)
Checks every active, verified StoreDomain entry.
| Example | What It Tests |
|---|---|
wizatech.shop |
Does detection_method=custom_domain resolve to the store that owns the domain? |
The Platform column shows the platform associated with the StoreDomain.platform_id relationship.
4. Path-Based Routes
Checks every active store's subdomain field via path-based resolution.
| Example | What It Tests |
|---|---|
/storefront/luxweb/ |
Does detection_method=path with subdomain=luxweb resolve to the correct store? |
/store/luxweb/ |
Same check for the store dashboard path |
The Platform column shows all platforms the store is a member of (via StorePlatform).
!!! note "Path segments use Store.subdomain"
Path-based URLs use the store's subdomain field, not store_code. For example, a store with store_code=LUXWEBSITES and subdomain=luxweb is accessed at /storefront/luxweb/, not /storefront/LUXWEBSITES/.
How It Works
The tool calls the same resolution functions that the live middleware uses:
graph LR
A[Health Check] -->|builds context dict| B[StoreContextManager.get_store_from_context]
B -->|queries DB| C[Store resolved?]
C -->|id matches expected| D[PASS]
C -->|mismatch or None| E[FAIL]
It does not make actual HTTP requests. This means it validates database configuration and resolution logic, but cannot detect:
- DNS misconfigurations
- Reverse proxy / Cloudflare routing issues
- SSL certificate problems
- Network-level failures
Reading the Results
Summary Bar
| Counter | Meaning |
|---|---|
| Total Checked | Number of access methods tested |
| Passed | Resolved to the expected store |
| Failed | Resolution mismatch or store not found |
Results Table
| Column | Description |
|---|---|
| Status | pass or fail badge |
| Domain | The domain, subdomain, or path being tested |
| Type | custom subdomain, default subdomain, custom domain, path (storefront), or path (store) |
| Platform | Platform code(s) the entry belongs to |
| Expected Store | The store code we expect to resolve |
| Resolved Store | The store code that actually resolved (or -- if none) |
| Note | Error description for failed entries |
Common Failure Scenarios
| Symptom | Likely Cause |
|---|---|
| Custom subdomain fails | StorePlatform.custom_subdomain doesn't match or StorePlatform.is_active=false |
| Default subdomain fails | Store has no active StorePlatform membership on the detected platform (cross-tenant blocked) |
| Custom domain fails | StoreDomain not verified, inactive, or store is inactive |
| Path-based fails | Store's subdomain field is empty or store is inactive |
| Resolved store differs from expected | Two stores have conflicting subdomain/domain entries |
API Response
GET /api/v1/admin/debug/domain-health
{
"total": 36,
"passed": 30,
"failed": 6,
"details": [
{
"domain": "acme-rewards.rewardflow.lu",
"type": "custom subdomain",
"platform_code": "loyalty",
"expected_store": "ACME",
"resolved_store": "ACME",
"status": "pass",
"note": ""
},
{
"domain": "acme.omsflow.lu",
"type": "default subdomain",
"platform_code": "oms",
"expected_store": "ACME",
"resolved_store": "ACME",
"status": "pass",
"note": ""
},
{
"domain": "/storefront/acme/",
"type": "path (storefront)",
"platform_code": "oms, loyalty",
"expected_store": "ACME",
"resolved_store": "ACME",
"status": "pass",
"note": ""
}
]
}
Related Documentation
- Middleware Stack - Full middleware pipeline reference
- Dev Tools Module - Other diagnostic tools
- Multi-Tenant System - Tenant detection architecture