Some checks failed
- Add Development URL Quick Reference section to url-routing overview with all login URLs, entry points, and full examples - Replace /shop/ path segments with /storefront/ across 50 docs files - Update file references: shop_pages.py → storefront_pages.py, templates/shop/ → templates/storefront/, api/v1/shop/ → api/v1/storefront/ - Preserve domain references (orion.shop) and /store/ staff dashboard paths - Archive docs left unchanged (historical) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
605 lines
17 KiB
Markdown
605 lines
17 KiB
Markdown
# Content Management System (CMS)
|
|
|
|
## Overview
|
|
|
|
The Content Management System allows platform administrators and stores to manage static content pages like About, FAQ, Contact, Shipping, Returns, Privacy Policy, Terms of Service, etc.
|
|
|
|
**Key Features:**
|
|
- ✅ Platform-level default content
|
|
- ✅ Store-specific overrides
|
|
- ✅ Fallback system (store → platform default)
|
|
- ✅ Rich text content (HTML/Markdown)
|
|
- ✅ SEO metadata
|
|
- ✅ Published/Draft status
|
|
- ✅ Navigation management (footer/header)
|
|
- ✅ Display order control
|
|
|
|
## Architecture
|
|
|
|
### Two-Tier Content System
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ CONTENT LOOKUP FLOW │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
|
|
Request: /about
|
|
|
|
1. Check for store-specific override
|
|
↓
|
|
SELECT * FROM content_pages
|
|
WHERE store_id = 123 AND slug = 'about' AND is_published = true
|
|
↓
|
|
Found? ✅ Return store content
|
|
❌ Continue to step 2
|
|
|
|
2. Check for platform default
|
|
↓
|
|
SELECT * FROM content_pages
|
|
WHERE store_id IS NULL AND slug = 'about' AND is_published = true
|
|
↓
|
|
Found? ✅ Return platform content
|
|
❌ Return 404 or default template
|
|
```
|
|
|
|
### Database Schema
|
|
|
|
```sql
|
|
CREATE TABLE content_pages (
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
-- Store association (NULL = platform default)
|
|
store_id INTEGER REFERENCES stores(id) ON DELETE CASCADE,
|
|
|
|
-- Page identification
|
|
slug VARCHAR(100) NOT NULL, -- about, faq, contact, shipping, returns
|
|
title VARCHAR(200) NOT NULL,
|
|
|
|
-- Content
|
|
content TEXT NOT NULL, -- HTML or Markdown
|
|
content_format VARCHAR(20) DEFAULT 'html', -- html, markdown
|
|
|
|
-- SEO
|
|
meta_description VARCHAR(300),
|
|
meta_keywords VARCHAR(300),
|
|
|
|
-- Publishing
|
|
is_published BOOLEAN DEFAULT FALSE NOT NULL,
|
|
published_at TIMESTAMP WITH TIME ZONE,
|
|
|
|
-- Navigation placement
|
|
display_order INTEGER DEFAULT 0,
|
|
show_in_footer BOOLEAN DEFAULT TRUE, -- Quick Links column
|
|
show_in_header BOOLEAN DEFAULT FALSE, -- Top navigation
|
|
show_in_legal BOOLEAN DEFAULT FALSE, -- Bottom bar with copyright
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
|
|
|
|
-- Author tracking
|
|
created_by INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
|
updated_by INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
|
|
|
-- Constraints
|
|
CONSTRAINT uq_store_slug UNIQUE (store_id, slug)
|
|
);
|
|
|
|
CREATE INDEX idx_store_published ON content_pages (store_id, is_published);
|
|
CREATE INDEX idx_slug_published ON content_pages (slug, is_published);
|
|
```
|
|
|
|
## Usage
|
|
|
|
### Platform Administrator Workflow
|
|
|
|
**1. Create Platform Default Pages**
|
|
|
|
Platform admins create default content that all stores inherit:
|
|
|
|
```bash
|
|
POST /api/v1/admin/content-pages/platform
|
|
{
|
|
"slug": "about",
|
|
"title": "About Us",
|
|
"content": "<h1>About Us</h1><p>We are a marketplace...</p>",
|
|
"content_format": "html",
|
|
"meta_description": "Learn more about our marketplace",
|
|
"is_published": true,
|
|
"show_in_header": true,
|
|
"show_in_footer": true,
|
|
"show_in_legal": false,
|
|
"display_order": 1
|
|
}
|
|
```
|
|
|
|
**Common Platform Defaults:**
|
|
- `about` - About Us
|
|
- `contact` - Contact Us
|
|
- `faq` - Frequently Asked Questions
|
|
- `shipping` - Shipping Information
|
|
- `returns` - Return Policy
|
|
- `privacy` - Privacy Policy
|
|
- `terms` - Terms of Service
|
|
- `help` - Help Center
|
|
|
|
**2. View All Content Pages**
|
|
|
|
```bash
|
|
GET /api/v1/admin/content-pages/
|
|
GET /api/v1/admin/content-pages/?store_id=123 # Filter by store
|
|
GET /api/v1/admin/content-pages/platform # Only platform defaults
|
|
```
|
|
|
|
**3. Update Platform Default**
|
|
|
|
```bash
|
|
PUT /api/v1/admin/content-pages/1
|
|
{
|
|
"title": "Updated About Us",
|
|
"content": "<h1>About Our Platform</h1>...",
|
|
"is_published": true
|
|
}
|
|
```
|
|
|
|
### Store Workflow
|
|
|
|
**1. View Available Pages**
|
|
|
|
Stores see their overrides + platform defaults:
|
|
|
|
```bash
|
|
GET /api/v1/store/{code}/content-pages/
|
|
```
|
|
|
|
Response:
|
|
```json
|
|
[
|
|
{
|
|
"id": 15,
|
|
"slug": "about",
|
|
"title": "About Orion", // Store override
|
|
"is_store_override": true,
|
|
"is_platform_page": false
|
|
},
|
|
{
|
|
"id": 2,
|
|
"slug": "shipping",
|
|
"title": "Shipping Information", // Platform default
|
|
"is_store_override": false,
|
|
"is_platform_page": true
|
|
}
|
|
]
|
|
```
|
|
|
|
**2. Create Store Override**
|
|
|
|
Store creates custom "About" page:
|
|
|
|
```bash
|
|
POST /api/v1/store/{code}/content-pages/
|
|
{
|
|
"slug": "about",
|
|
"title": "About Orion",
|
|
"content": "<h1>About Orion</h1><p>We specialize in...</p>",
|
|
"is_published": true
|
|
}
|
|
```
|
|
|
|
This overrides the platform default for this store only.
|
|
|
|
**3. View Only Store Overrides**
|
|
|
|
```bash
|
|
GET /api/v1/store/{code}/content-pages/overrides
|
|
```
|
|
|
|
Shows what the store has customized (excludes platform defaults).
|
|
|
|
**4. Delete Override (Revert to Platform Default)**
|
|
|
|
```bash
|
|
DELETE /api/v1/store/{code}/content-pages/15
|
|
```
|
|
|
|
After deletion, platform default will be shown again.
|
|
|
|
### Storefront (Public)
|
|
|
|
**1. Get Page Content**
|
|
|
|
```bash
|
|
GET /api/v1/storefront/content-pages/about
|
|
```
|
|
|
|
Automatically uses store context from middleware:
|
|
- Returns store override if exists
|
|
- Falls back to platform default
|
|
- Returns 404 if neither exists
|
|
|
|
**2. Get Navigation Links**
|
|
|
|
```bash
|
|
# Get all navigation pages
|
|
GET /api/v1/storefront/content-pages/navigation
|
|
|
|
# Filter by placement
|
|
GET /api/v1/storefront/content-pages/navigation?header_only=true
|
|
GET /api/v1/storefront/content-pages/navigation?footer_only=true
|
|
GET /api/v1/storefront/content-pages/navigation?legal_only=true
|
|
```
|
|
|
|
Returns published pages filtered by navigation placement.
|
|
|
|
## File Structure
|
|
|
|
```
|
|
app/
|
|
├── models/database/
|
|
│ └── content_page.py ← Database model
|
|
│
|
|
├── services/
|
|
│ └── content_page_service.py ← Business logic
|
|
│
|
|
├── api/v1/
|
|
│ ├── admin/
|
|
│ │ └── content_pages.py ← Admin API endpoints
|
|
│ ├── store/
|
|
│ │ └── content_pages.py ← Store API endpoints
|
|
│ └── storefront/
|
|
│ └── content_pages.py ← Public API endpoints
|
|
│
|
|
└── templates/storefront/
|
|
├── about.html ← Content page template
|
|
├── faq.html
|
|
├── contact.html
|
|
└── ...
|
|
```
|
|
|
|
## Template Integration
|
|
|
|
### Generic Content Page Template
|
|
|
|
Create a reusable template for all content pages:
|
|
|
|
```jinja2
|
|
{# app/templates/storefront/content-page.html #}
|
|
{% extends "storefront/base.html" %}
|
|
|
|
{% block title %}{{ page.title }}{% endblock %}
|
|
|
|
{% block meta_description %}
|
|
{{ page.meta_description or page.title }}
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
|
|
|
{# Breadcrumbs #}
|
|
<nav class="mb-6">
|
|
<a href="{{ base_url }}" class="text-primary hover:underline">Home</a>
|
|
<span class="mx-2">/</span>
|
|
<span class="text-gray-600">{{ page.title }}</span>
|
|
</nav>
|
|
|
|
{# Page Title #}
|
|
<h1 class="text-4xl font-bold text-gray-900 dark:text-gray-100 mb-8">
|
|
{{ page.title }}
|
|
</h1>
|
|
|
|
{# Content #}
|
|
<div class="prose dark:prose-invert max-w-none">
|
|
{% if page.content_format == 'markdown' %}
|
|
{{ page.content | markdown }}
|
|
{% else %}
|
|
{{ page.content | safe }}
|
|
{% endif %}
|
|
</div>
|
|
|
|
{# Last updated #}
|
|
{% if page.updated_at %}
|
|
<div class="mt-12 pt-6 border-t text-sm text-gray-500">
|
|
Last updated: {{ page.updated_at }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
</div>
|
|
{% endblock %}
|
|
```
|
|
|
|
### Route Handler
|
|
|
|
```python
|
|
# app/routes/storefront_pages.py
|
|
|
|
from app.services.content_page_service import content_page_service
|
|
|
|
@router.get("/{slug}", response_class=HTMLResponse)
|
|
async def content_page(
|
|
slug: str,
|
|
request: Request,
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""
|
|
Generic content page handler.
|
|
|
|
Loads content from database with store override support.
|
|
"""
|
|
store = getattr(request.state, 'store', None)
|
|
store_id = store.id if store else None
|
|
|
|
page = content_page_service.get_page_for_store(
|
|
db,
|
|
slug=slug,
|
|
store_id=store_id,
|
|
include_unpublished=False
|
|
)
|
|
|
|
if not page:
|
|
raise HTTPException(status_code=404, detail=f"Page not found: {slug}")
|
|
|
|
return templates.TemplateResponse(
|
|
"storefront/content-page.html",
|
|
get_storefront_context(request, page=page)
|
|
)
|
|
```
|
|
|
|
### Dynamic Footer Navigation
|
|
|
|
Update footer to load links from database:
|
|
|
|
```jinja2
|
|
{# app/templates/storefront/base.html #}
|
|
|
|
<footer>
|
|
<div class="grid grid-cols-3">
|
|
|
|
<div>
|
|
<h4>Quick Links</h4>
|
|
<ul>
|
|
{% for page in footer_pages %}
|
|
<li>
|
|
<a href="{{ base_url }}{{ page.slug }}">
|
|
{{ page.title }}
|
|
</a>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
|
|
</div>
|
|
</footer>
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### 1. Content Formatting
|
|
|
|
**HTML Content:**
|
|
```html
|
|
<h1>About Us</h1>
|
|
<p>We are a <strong>leading marketplace</strong> for...</p>
|
|
<ul>
|
|
<li>Quality products</li>
|
|
<li>Fast shipping</li>
|
|
<li>Great support</li>
|
|
</ul>
|
|
```
|
|
|
|
**Markdown Content:**
|
|
```markdown
|
|
# About Us
|
|
|
|
We are a **leading marketplace** for...
|
|
|
|
- Quality products
|
|
- Fast shipping
|
|
- Great support
|
|
```
|
|
|
|
### 2. SEO Optimization
|
|
|
|
Always provide meta descriptions:
|
|
|
|
```json
|
|
{
|
|
"meta_description": "Learn about our marketplace, mission, and values. We connect stores with customers worldwide.",
|
|
"meta_keywords": "about us, marketplace, e-commerce, mission"
|
|
}
|
|
```
|
|
|
|
### 3. Draft → Published Workflow
|
|
|
|
1. Create page with `is_published: false`
|
|
2. Preview using `include_unpublished=true` parameter
|
|
3. Review and edit
|
|
4. Publish with `is_published: true`
|
|
|
|
### 4. Navigation Management
|
|
|
|
The CMS supports three navigation placement categories:
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ HEADER (show_in_header=true) │
|
|
│ [Logo] About Us Contact [Login] [Sign Up] │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ PAGE CONTENT │
|
|
│ │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ FOOTER (show_in_footer=true) │
|
|
│ ┌──────────────┬──────────────┬────────────┬──────────────┐ │
|
|
│ │ Quick Links │ Platform │ Contact │ Social │ │
|
|
│ │ • About │ • Admin │ • Email │ • Twitter │ │
|
|
│ │ • FAQ │ • Store │ • Phone │ • LinkedIn │ │
|
|
│ │ • Contact │ │ │ │ │
|
|
│ │ • Shipping │ │ │ │ │
|
|
│ └──────────────┴──────────────┴────────────┴──────────────┘ │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ LEGAL BAR (show_in_legal=true) │
|
|
│ © 2025 Orion Privacy Policy │ Terms │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
**Navigation Categories:**
|
|
|
|
| Category | Field | Location | Typical Pages |
|
|
|----------|-------|----------|---------------|
|
|
| Header | `show_in_header` | Top navigation bar | About, Contact |
|
|
| Footer | `show_in_footer` | Quick Links column | FAQ, Shipping, Returns |
|
|
| Legal | `show_in_legal` | Bottom bar with © | Privacy, Terms |
|
|
|
|
**Use `display_order` to control link ordering within each category:**
|
|
|
|
```python
|
|
# Platform defaults with navigation placement
|
|
"about": display_order=1, show_in_header=True, show_in_footer=True
|
|
"contact": display_order=2, show_in_header=True, show_in_footer=True
|
|
"faq": display_order=3, show_in_footer=True
|
|
"shipping": display_order=4, show_in_footer=True
|
|
"returns": display_order=5, show_in_footer=True
|
|
"privacy": display_order=6, show_in_legal=True
|
|
"terms": display_order=7, show_in_legal=True
|
|
```
|
|
|
|
### 5. Content Reversion
|
|
|
|
To revert store override back to platform default:
|
|
|
|
```bash
|
|
# Store deletes their custom page
|
|
DELETE /api/v1/store/{code}/content-pages/15
|
|
|
|
# Platform default will now be shown automatically
|
|
```
|
|
|
|
## Common Page Slugs
|
|
|
|
Standard slugs to implement:
|
|
|
|
| Slug | Title | Header | Footer | Legal | Order |
|
|
|------|-------|--------|--------|-------|-------|
|
|
| `about` | About Us | ✅ | ✅ | ❌ | 1 |
|
|
| `contact` | Contact Us | ✅ | ✅ | ❌ | 2 |
|
|
| `faq` | FAQ | ❌ | ✅ | ❌ | 3 |
|
|
| `shipping` | Shipping Info | ❌ | ✅ | ❌ | 4 |
|
|
| `returns` | Returns | ❌ | ✅ | ❌ | 5 |
|
|
| `privacy` | Privacy Policy | ❌ | ❌ | ✅ | 6 |
|
|
| `terms` | Terms of Service | ❌ | ❌ | ✅ | 7 |
|
|
| `help` | Help Center | ❌ | ✅ | ❌ | 8 |
|
|
| `size-guide` | Size Guide | ❌ | ❌ | ❌ | - |
|
|
| `careers` | Careers | ❌ | ❌ | ❌ | - |
|
|
| `cookies` | Cookie Policy | ❌ | ❌ | ✅ | 8 |
|
|
|
|
## Security Considerations
|
|
|
|
1. **HTML Sanitization**: If using HTML format, sanitize user input to prevent XSS
|
|
2. **Authorization**: Stores can only edit their own pages
|
|
3. **Published Status**: Only published pages visible to public
|
|
4. **Store Isolation**: Stores cannot see/edit other store's content
|
|
|
|
## Migration Strategy
|
|
|
|
### Initial Setup
|
|
|
|
1. **Create Platform Defaults**:
|
|
```bash
|
|
python scripts/seed/create_default_content_pages.py
|
|
```
|
|
|
|
2. **Migrate Existing Static Templates**:
|
|
- Convert existing HTML templates to database content
|
|
- Preserve existing URLs and SEO
|
|
|
|
3. **Update Routes**:
|
|
- Add generic content page route handler
|
|
- Remove individual route handlers for each page
|
|
|
|
## Future Enhancements
|
|
|
|
Possible improvements:
|
|
|
|
- **Version History**: Track content changes over time
|
|
- **Rich Text Editor**: WYSIWYG editor in admin/store panel
|
|
- **Image Management**: Upload and insert images
|
|
- **Templates**: Pre-built page templates for common pages
|
|
- **Localization**: Multi-language content support
|
|
- **Scheduled Publishing**: Publish pages at specific times
|
|
- **Content Approval**: Admin review before store pages go live
|
|
|
|
## API Reference Summary
|
|
|
|
### Admin Endpoints
|
|
|
|
```
|
|
GET /api/v1/admin/content-pages/ # List all pages
|
|
GET /api/v1/admin/content-pages/platform # List platform defaults
|
|
POST /api/v1/admin/content-pages/platform # Create platform default
|
|
GET /api/v1/admin/content-pages/{id} # Get specific page
|
|
PUT /api/v1/admin/content-pages/{id} # Update page
|
|
DELETE /api/v1/admin/content-pages/{id} # Delete page
|
|
```
|
|
|
|
### Store Endpoints
|
|
|
|
```
|
|
GET /api/v1/store/{code}/content-pages/ # List all (store + platform)
|
|
GET /api/v1/store/{code}/content-pages/overrides # List store overrides only
|
|
GET /api/v1/store/{code}/content-pages/{slug} # Get specific page
|
|
POST /api/v1/store/{code}/content-pages/ # Create store override
|
|
PUT /api/v1/store/{code}/content-pages/{id} # Update store page
|
|
DELETE /api/v1/store/{code}/content-pages/{id} # Delete store page
|
|
```
|
|
|
|
### Storefront (Public) Endpoints
|
|
|
|
```
|
|
GET /api/v1/storefront/content-pages/navigation # Get navigation links
|
|
GET /api/v1/storefront/content-pages/{slug} # Get page content
|
|
```
|
|
|
|
## Example: Complete Workflow
|
|
|
|
**1. Platform Admin Creates Defaults:**
|
|
```bash
|
|
# Create "About" page
|
|
curl -X POST /api/v1/admin/content-pages/platform \
|
|
-H "Authorization: Bearer <admin_token>" \
|
|
-d '{
|
|
"slug": "about",
|
|
"title": "About Our Marketplace",
|
|
"content": "<h1>About</h1><p>Default content...</p>",
|
|
"is_published": true
|
|
}'
|
|
```
|
|
|
|
**2. All Stores See Platform Default:**
|
|
- Store A visits: `store-a.com/about` → Shows platform default
|
|
- Store B visits: `store-b.com/about` → Shows platform default
|
|
|
|
**3. Store A Creates Override:**
|
|
```bash
|
|
curl -X POST /api/v1/store/store-a/content-pages/ \
|
|
-H "Authorization: Bearer <store_token>" \
|
|
-d '{
|
|
"slug": "about",
|
|
"title": "About Store A",
|
|
"content": "<h1>About Store A</h1><p>Custom content...</p>",
|
|
"is_published": true
|
|
}'
|
|
```
|
|
|
|
**4. Now:**
|
|
- Store A visits: `store-a.com/about` → Shows Store A custom content
|
|
- Store B visits: `store-b.com/about` → Still shows platform default
|
|
|
|
**5. Store A Reverts to Default:**
|
|
```bash
|
|
curl -X DELETE /api/v1/store/store-a/content-pages/15 \
|
|
-H "Authorization: Bearer <store_token>"
|
|
```
|
|
|
|
**6. Result:**
|
|
- Store A visits: `store-a.com/about` → Shows platform default again
|